{"record":{"id":"ea25b1de5dc9fd86","repo":"qishibo/AnotherRedisDesktopManager","slug":"e-message","errorCode":null,"errorMessage":"e.message","messagePattern":"e\\.message","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/components/KeyList.vue","lineNumber":243,"sourceCode":"      this.$parent.$parent.$parent.$refs.operateItem.searchIcon = 'el-icon-loading';\n      // show cancel scanning btn after scanning for a while\n      this.$parent.$parent.$parent.$refs.operateItem.toggleCancelIcon(true);\n    },\n    resetSearchStatus() {\n      // search input icon recover\n      this.$parent.$parent.$parent.$refs.operateItem.searchIcon = 'el-icon-search';\n      // remove cancel scanning btn\n      this.$parent.$parent.$parent.$refs.operateItem.toggleCancelIcon(false);\n      // reset loading all status\n      this.loadingAll = false;\n    },\n    refreshKeyListExact() {\n      const match = this.getMatchMode(false);\n\n      this.client.exists(match).then((reply) => {\n        this.keyList = (reply == 1) ? [Buffer.from(match)] : [];\n      }).catch((e) => {\n        this.$message.error(e.message);\n      }).finally(() => {\n        this.scanMoreDisabled = true;\n        this.resetSearchStatus();\n      });\n    },\n    cancelScanning() {\n      if (this.scanStreams.length) {\n        for (const stream of this.scanStreams) {\n          stream.pause && stream.pause();\n        }\n      }\n    },\n    getMatchMode(fillStar = true) {\n      let match = this.$parent.$parent.$parent.$refs.operateItem.searchMatch;\n\n      match = match || '*';\n\n      if (fillStar && !match.match(/\\*/)) {","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/qishibo/AnotherRedisDesktopManager/blob/c149855106628babcdfb7675a8e7ba9434d2492a/src/components/KeyList.vue#L225-L261","documentation":"In exact-match mode (refreshKeyListExact in KeyList.vue) the app replaces SCAN with a single client.exists(match) call; if that promise rejects, the raw server/client error message is shown in a toast. EXISTS is a read command, so the app's readonly guard does not apply - the rejection comes from the connection or the server.","triggerScenarios":"Searching exactly when the connection has already dropped (the catch fires before closeConnection finishes); an ACL user denied EXISTS (-exists / missing @keyspace); server unavailable or restarting; proxy rejecting the command.","commonSituations":"Typing in the search box right after a network change; restricted ACL users created without the keyspace category; searching on a connection the server just closed (timeout).","solutions":["Reconnect and retry the exact search.","If the message is 'NOPERM' or similar, grant the ACL user the @keyspace/@read categories.","Confirm the server is up with a trivial command (PING) in the CLI tab."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// verify the connection is alive before exact-match search\nawait client.ping(); // throws early with a clear cause if the connection is dead\nconst reply = await client.exists(match);","typeGuard":"const keyExists = (reply) => reply === 1;","tryCatchPattern":"this.client.exists(match)\n  .then((reply) => { this.keyList = keyExists(reply) ? [Buffer.from(match)] : []; })\n  .catch((e) => {\n    this.$message.error(e.message);\n    // connection is likely dead: verify with ping before retrying\n  })\n  .finally(() => this.resetSearchStatus());","preventionTips":["Guard exact-search UI behind a liveness check (PING) so users get a clear reconnect prompt instead of a raw server error.","For ACL users, include +exists/@keyspace in the grant set."],"tags":["exists","exact-match","connection-loss","acl"],"backgroundTag":"connection-lost-during-operation","analyzedSha":"c149855106628babcdfb7675a8e7ba9434d2492a","analyzedAt":"2026-08-22T09:06:28.613Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}