{"record":{"id":"4d2b32b25c361d4a","repo":"can1357/oh-my-pi","slug":"send-requires-data-or-signal","errorCode":null,"errorMessage":"send requires data or signal","messagePattern":"send requires data or signal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/broker.ts","lineNumber":1117,"sourceCode":"\t\t\t\t`Daemon ${operation.name} generation ${boundGeneration} exited${exit}; ` +\n\t\t\t\t\t\"the wait was rejected instead of continuing against a replacement generation\",\n\t\t\t);\n\t\t}\n\t\t// A for:\"ready\" wait that woke on a terminal exit without ever observing\n\t\t// readiness is still \"not ready\" — surface it as timed out so callers and the\n\t\t// renderer don't chain work against a dead process.\n\t\tconst timedOut = operation.for === \"ready\" && !pattern ? !readyObserved() : !woke;\n\t\treturn { op: \"wait\", daemon: record.snapshot, matched, timedOut };\n\t}\n\n\tasync #send(operation: Extract<DaemonOperation, { op: \"send\" }>): Promise<DaemonRpcResult> {\n\t\tconst record = this.#record(operation.name);\n\t\tawait this.#refreshDetached(record);\n\t\tif (terminalState(record.snapshot.state) || record.snapshot.state === \"stopping\") {\n\t\t\tthrow new Error(`Daemon ${operation.name} is ${record.snapshot.state}`);\n\t\t}\n\t\tif (operation.data === undefined && operation.signal === undefined) {\n\t\t\tthrow new Error(\"send requires data or signal\");\n\t\t}\n\t\tif (operation.data !== undefined) {\n\t\t\tif (record.pty) record.pty.write(operation.data);\n\t\t\telse if (record.input) {\n\t\t\t\trecord.input.write(operation.data);\n\t\t\t\tawait record.input.flush();\n\t\t\t} else throw new Error(`Daemon ${operation.name} stdin is unavailable`);\n\t\t}\n\t\tif (operation.signal) {\n\t\t\tif (process.platform === \"win32\" && record.pty) {\n\t\t\t\tif (operation.signal === \"SIGINT\") record.pty.write(\"\\u0003\");\n\t\t\t\telse record.pty.kill();\n\t\t\t} else {\n\t\t\t\tconst processRef = record.snapshot.pid === undefined ? null : Process.fromPid(record.snapshot.pid);\n\t\t\t\tif (!processRef) throw new Error(`Daemon ${operation.name} process is unavailable`);\n\t\t\t\tprocessRef.killTree(SIGNAL_NUMBER[operation.signal]);\n\t\t\t}\n\t\t}","sourceCodeStart":1099,"sourceCodeEnd":1135,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/broker.ts#L1099-L1135","documentation":"A send operation must carry something to deliver: either data (stdin/pty input) or a signal. DaemonBroker throws this fixed-message error when operation.data and operation.signal are both undefined, since there is nothing to do. It is a request-validation guard in #send.","triggerScenarios":"Calling send with an empty payload — e.g. { op: \"send\", name: \"x\" } with neither data nor signal, or data explicitly set to undefined after conditional construction.","commonSituations":"Code that builds the send op conditionally (`...(cond ? { data } : {})`) where cond was false; deserialized RPC requests that dropped empty-string fields; wiring bugs passing the wrong variable.","solutions":["Provide the data string you intended to write to the daemon's stdin/pty.","Provide the signal you intended to deliver (e.g. \"SIGINT\").","Guard the call site: only issue a send op when at least one of data/signal is defined."],"exampleFix":"// before\nconst op = { op: \"send\", name: \"repl\" }; // data lost in refactor\n// after\nconst op = { op: \"send\", name: \"repl\", data: \"run()\\n\" };","handlingStrategy":"validation","validationCode":"if (op.data === undefined && op.signal === undefined) {\n  throw new Error(\"send op needs data or signal\");\n}","typeGuard":"function isDeliverableSend(op: { data?: string; signal?: string }): boolean {\n  return op.data !== undefined || op.signal !== undefined;\n}","tryCatchPattern":"try {\n  await broker.send(op);\n} catch (err) {\n  if (err instanceof Error && err.message === \"send requires data or signal\") {\n    // caller bug: reconstruct the op with a payload\n  } else throw err;\n}","preventionTips":["Build send ops through a helper function that requires at least one of data/signal at the type level.","Avoid spreading conditional objects that can silently drop the data field.","Cover the send call site with a test that asserts a payload is always present."],"tags":["validation","ipc","api-misuse"],"backgroundTag":"missing-required-parameter","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}