{"record":{"id":"d1657f896855ccbc","repo":"can1357/oh-my-pi","slug":"unexpected-broker-response-first-op","errorCode":null,"errorMessage":"Unexpected broker response ${first.op}","messagePattern":"Unexpected broker response (.+?)","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/ps-cli.ts","lineNumber":229,"sourceCode":"\tif (cmd.flags.json) console.log(JSON.stringify(daemon, null, 2));\n\telse console.log(`${verb} ${daemonLabel(daemon)}`);\n}\n\nasync function runLogs(cmd: PsCommandArgs, client: DaemonBrokerClient, name: string): Promise<void> {\n\tconst lines = Math.max(1, Math.min(1_000, Math.floor(cmd.flags.lines ?? 100)));\n\t// Follow mode reads the full 1000-line window on every request so overlap\n\t// trimming sees a stable, sliding tail; the initial print is cut to `lines`.\n\tconst first = await client.request({\n\t\top: \"logs\",\n\t\tname,\n\t\tlines: cmd.flags.follow ? 1_000 : lines,\n\t\thead: cmd.flags.head,\n\t\tgrep: cmd.flags.grep,\n\t\tfollow: false,\n\t\trenderTerminalRows: !cmd.flags.follow,\n\t\ttimeoutMs: 30_000,\n\t});\n\tif (first.op !== \"logs\") throw new Error(`Unexpected broker response ${first.op}`);\n\tif (!cmd.flags.follow) {\n\t\tconst text = first.terminalRows !== undefined ? first.terminalRows.join(\"\\n\") : first.text.replace(/\\n$/, \"\");\n\t\tif (text) console.log(text);\n\t\tconsole.log(chalk.dim(`[${name}: ${first.state}]`));\n\t\treturn;\n\t}\n\tconst initial = first.text.replace(/\\n$/, \"\").split(\"\\n\").slice(-lines).join(\"\\n\");\n\tif (initial) process.stdout.write(`${initial}\\n`);\n\tlet previous = first.text;\n\tlet cursor = first.cursor;\n\tlet state = first.state;\n\twhile (!TERMINAL_STATES[state]) {\n\t\tconst next = await client.request({\n\t\t\top: \"logs\",\n\t\t\tname,\n\t\t\tlines: 1_000,\n\t\t\thead: false,\n\t\t\tgrep: cmd.flags.grep,","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/ps-cli.ts#L211-L247","documentation":"`omp ps logs` issues an initial `{op:\"logs\"}` request and requires a logs-shaped reply to read `first.text` / `first.terminalRows` / `first.state`. This guard throws when the broker answers with any other op, so the log payload is absent and printing would read undefined fields.","triggerScenarios":"`runLogs` where the first `client.request({op:\"logs\", ..., follow:false})` resolves with a response whose `op` is not \"logs\" — e.g. an error/not-found response, or a broker running an incompatible protocol version.","commonSituations":"Fetching logs for a daemon that the connected broker doesn't own (wrong `--dir`/`--global` scope so the name resolves differently); stale broker after an omp upgrade; the daemon exited and was pruned so the broker replies with an error op.","solutions":["Point the command at the right scope: pass `--dir <project>` or `--global <service>` matching where the daemon runs, and use the exact name from `omp ps --all`.","Restart/reconnect the broker so CLI and broker protocol versions match.","If the daemon has exited, check whether its metadata still exists (`<runtimeDir>/daemons/<name>/meta.json`) or view persisted logs directly from that directory."],"exampleFix":"// before\nconst first = await client.request({ op: \"logs\", ... });\nif (first.op !== \"logs\") throw new Error(`Unexpected broker response ${first.op}`);\n// after\nconst first = await client.request({ op: \"logs\", ... });\nif (first.op === \"error\") throw new Error(`Cannot read logs: ${first.message}`);\nif (first.op !== \"logs\") throw new Error(`Unexpected broker response ${first.op}`);","handlingStrategy":"type-guard","validationCode":"const listing = await client.request({ op: \"list\" });\nif (listing.op === \"list\" && !listing.daemons.some(d => d.name === name)) {\n  throw new Error(`No daemon \"${name}\" in this scope; check --dir/--global`);\n}","typeGuard":"function isLogsResponse(r: { op: string }): r is { op: \"logs\"; text: string; terminalRows?: string[]; state: string; cursor?: string } {\n  return r.op === \"logs\";\n}","tryCatchPattern":"try {\n  const first = await client.request({ op: \"logs\", name, follow: false, timeoutMs: 30_000 });\n  if (!isLogsResponse(first)) throw new Error(`Unexpected broker response ${first.op}`);\n} catch (err) {\n  console.error(chalk.red(err instanceof Error ? err.message : String(err)));\n  process.exitCode = 1;\n}","preventionTips":["Target the correct scope with --dir/--global and the exact daemon name from `omp ps --all`.","Handle error-op replies explicitly to distinguish 'daemon gone' from protocol mismatch.","Restart brokers after upgrading omp."],"tags":["ipc","logs","protocol-mismatch"],"backgroundTag":"unexpected-response-op","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}