{"record":{"id":"1aef8ed447439e5f","repo":"jackwener/OpenCLI","slug":"no-thread-should-be-handled-by-caller-not-dispatc","errorCode":null,"errorMessage":"no-thread should be handled by caller, not dispatcher: ${r.parent}","messagePattern":"no-thread should be handled by caller, not dispatcher: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/slock/errors.js","lineNumber":25,"sourceCode":"//   { kind: 'http', status, where }\n//   { kind: 'no-server', detail }\n//   { kind: 'unresolvable', detail }\n//   { kind: 'no-thread', parent }       // handled by caller, never reaches dispatcher\nexport function dispatchEvaluateResult(r) {\n  switch (r && r.kind) {\n    case 'ok':\n      return r.rows;\n    case 'auth':\n      throw new AuthRequiredError(SLOCK_DOMAIN, r.detail);\n    case 'http':\n      throw new CommandExecutionError(`HTTP ${r.status} from ${r.where}`);\n    case 'no-server':\n      throw new ConfigError(r.detail, 'Run `opencli slock server-use <slug>` to set the active server.');\n    case 'unresolvable':\n      throw new ArgumentError(r.detail);\n    case 'no-thread':\n      // caller decides what to do (returns a 0-row hint); should never reach here\n      throw new CommandExecutionError(`no-thread should be handled by caller, not dispatcher: ${r.parent}`);\n    default:\n      // unknown / null envelope = contract drift; fail loud (reddit precedent)\n      throw new CommandExecutionError(`unexpected evaluate envelope: ${JSON.stringify(r)}`);\n  }\n}\n","sourceCodeStart":7,"sourceCodeEnd":31,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/slock/errors.js#L7-L31","documentation":"The 'no-thread' envelope is a contract-level sentinel that callers are supposed to intercept and turn into a 0-row hint. If it reaches dispatchEvaluateResult, it throws CommandExecutionError with this defensive message plus the parent reference. This is an internal contract-violation guard, indicating a bug in the calling command's flow, not a user error.","triggerScenarios":"A command built with makeThreadStateCommand (or a caller of dispatchEvaluateResult) failed to check r.kind === 'no-thread' before dispatching; a refactor dropped the caller-side no-thread branch.","commonSituations":"Thread-scoped commands run against a channel where no thread/parent exists and the caller didn't handle the sentinel; running an outdated CLI version with mismatched dispatcher/caller code.","solutions":["Upgrade the slock CLI — this indicates an internal bug likely fixed upstream","Report the bug with the command and arguments used; include the r.parent value in the message","As a workaround, use a command targeting an existing thread/channel"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// caller-side: intercept the sentinel before dispatching\nif (result && result.kind === 'no-thread') return []; // 0-row hint, never call dispatchEvaluateResult","typeGuard":"const isNoThreadEnvelope = (r) => r != null && typeof r === 'object' && r.kind === 'no-thread';","tryCatchPattern":"try { return dispatchEvaluateResult(result); } catch (e) { if (/no-thread should be handled by caller/.test(e.message)) { console.error('CLI bug: update the slock CLI'); return []; } throw e; }","preventionTips":["Always check r.kind === 'no-thread' before calling the dispatcher","Keep caller templates (makeThreadStateCommand) and dispatcher in the same version","Report this as a bug — it indicates internal contract violation"],"tags":["contract-drift","internal-bug","cli"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}