{"record":{"id":"84991661dd275d15","repo":"qishibo/AnotherRedisDesktopManager","slug":"you-are-in-readonly-mode-unable-to-execute-write","errorCode":null,"errorMessage":"You are in readonly mode! Unable to execute write command!","messagePattern":"You are in readonly mode! Unable to execute write command!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/redisClient.js","lineNumber":17,"sourceCode":"import Redis from 'ioredis';\nimport { createTunnel } from 'tunnel-ssh';\nimport vue from '@/main.js';\nimport { remote } from 'electron';\nimport { writeCMD } from '@/commands.js';\n\nconst fs = require('fs');\n\nconst { sendCommand } = Redis.prototype;\n\n// redis command log\nRedis.prototype.sendCommand = function (...options) {\n  const command = options[0];\n\n  // readonly mode\n  if (this.options.connectionReadOnly && writeCMD[command.name.toUpperCase()]) {\n    command.reject(new Error('You are in readonly mode! Unable to execute write command!'));\n    return command.promise;\n  }\n\n  // exec directly, without logs\n  if (this.withoutLogging === true) {\n    // invalid in next calling\n    this.withoutLogging = false;\n    return sendCommand.apply(this, options);\n  }\n\n  const start = performance.now();\n  const response = sendCommand.apply(this, options);\n  const cost = performance.now() - start;\n\n  const record = {\n    time: new Date(), connectionName: this.options.connectionName, command, cost,\n  };\n  vue.$bus.$emit('commandLog', record);","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/qishibo/AnotherRedisDesktopManager/blob/c149855106628babcdfb7675a8e7ba9434d2492a/src/redisClient.js#L1-L35","documentation":"This is a client-side guard inside the app's patched Redis.prototype.sendCommand (src/redisClient.js). When the connection was created with connectionReadOnly enabled, any command whose uppercase name exists in the writeCMD table from src/commands.js (DEL, SET, HSET, EXPIRE, PERSIST, RENAME, FLUSHDB, FLUSHALL, ...) is rejected before it ever reaches the server; command.reject() is called with this exact message. The command promise rejects, so every UI write action (edit value, delete, rename, set TTL, import keys) fails with it.","triggerScenarios":"Connection saved with the Readonly option enabled, then performing any intercepted write: editing a key value (SET/HSET/LPUSH), deleting a key (DEL/UNLINK), renaming (RENAME), changing TTL (EXPIRE/PERSIST), FLUSHDB/FLUSHALL, or running a write command in the CLI tab.","commonSituations":"Connecting to production Redis with readonly deliberately checked to prevent accidents, then forgetting it is on; toggling readonly in the connection config without appreciating that the block is unconditional; shared prod credentials where only read access was intended.","solutions":["Edit the connection settings, uncheck Readonly, and reconnect - writes then pass through to the server.","If readonly must stay on, route write operations to a separate writable connection instead of trying to bypass the guard.","Check the exact command name against the writeCMD list in src/commands.js to confirm which operations are blocked.","Programmatically inspect client.options.connectionReadOnly before issuing writes and warn the user early."],"exampleFix":"// before\nclient.set(key, value).catch(e => showMessage(e.message)); // rejects: readonly mode\n\n// after\nimport { writeCMD } from '@/commands.js';\nif (client.options.connectionReadOnly && writeCMD['SET']) {\n  showWarning('Connection is readonly - disable Readonly in connection settings to write.');\n} else {\n  client.set(key, value);\n}","handlingStrategy":"validation","validationCode":"import { writeCMD } from '@/commands.js';\n\n// run before any write command\nfunction assertWritable(client, commandName) {\n  if (client.options.connectionReadOnly && writeCMD[commandName.toUpperCase()]) {\n    throw new Error(`'${commandName}' is blocked: connection is readonly. Disable Readonly in connection settings.`);\n  }\n}","typeGuard":"const isReadonlyRejection = (e) => e instanceof Error && e.message.includes('readonly mode');","tryCatchPattern":"try {\n  await client.set(key, value);\n} catch (e) {\n  if (isReadonlyRejection(e)) {\n    promptUserToDisableReadonly(); // config fix, not a retry\n  } else {\n    throw e;\n  }\n}","preventionTips":["Check client.options.connectionReadOnly before surfacing write actions in the UI.","Keep two saved connections (read-only and writable) for production instead of toggling one.","Remember the guard is name-table based: any command in writeCMD is blocked, including EXPIRE, PERSIST, RENAME and DEL."],"tags":["readonly","write-protection","client-side-guard","redis"],"backgroundTag":"read-only-mode-violation","analyzedSha":"c149855106628babcdfb7675a8e7ba9434d2492a","analyzedAt":"2026-08-22T09:06:28.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}