{"record":{"id":"8f18b73adb32bbdd","repo":"can1357/oh-my-pi","slug":"this-options-languagename-kernel-stdin-is-not","errorCode":null,"errorMessage":"${this.#options.languageName} kernel stdin is not open","messagePattern":"(.+?) kernel stdin is not open","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/kernel-base.ts","lineNumber":423,"sourceCode":"\t\tfor (const entry of pending) {\n\t\t\tif (entry.settled) continue;\n\t\t\tentry.settled = true;\n\t\t\tvoid entry.options?.onChunk?.(`[kernel] ${reason}\\n`);\n\t\t\tentry.resolve({\n\t\t\t\tstatus: \"error\",\n\t\t\t\tcancelled: true,\n\t\t\t\ttimedOut: entry.timedOut,\n\t\t\t\tstdinRequested: entry.stdinRequested,\n\t\t\t\texecutionCount: entry.executionCount,\n\t\t\t\terror: entry.error,\n\t\t\t\tkernelKilled: entry.kernelKilled || kernelKilledDefault,\n\t\t\t});\n\t\t}\n\t}\n\n\tasync #writeLine(line: string): Promise<void> {\n\t\tif (!this.#stdin) {\n\t\t\tthrow new Error(`${this.#options.languageName} kernel stdin is not open`);\n\t\t}\n\t\tif (this.#options.traceIpc) {\n\t\t\tlogger.debug(`${this.#options.languageName}Kernel send`, { preview: line.slice(0, 120) });\n\t\t}\n\t\tthis.#stdin.write(`${line}\\n`);\n\t\tthis.#stdin.flush();\n\t}\n\n\t#startReader(stream: ReadableStream<Uint8Array>): void {\n\t\tconst reader = stream.getReader();\n\t\tconst decoder = new TextDecoder();\n\t\tconst loop = async () => {\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\t\tif (done) break;\n\t\t\t\t\tthis.#readBuffer += decoder.decode(value, { stream: true });\n\t\t\t\t\tawait this.#flushFrames();","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/kernel-base.ts#L405-L441","documentation":"`#writeLine` writes newline-delimited IPC messages to the kernel's stdin stream. When `#stdin` is null — kernel not started, stdin already closed on shutdown, or the process died and streams were torn down — it throws `\"<language> kernel stdin is not open\"`. Both `execute` (sending code) and `shutdown` (sending the exit command) go through this path.","triggerScenarios":"Calling `execute` on a kernel whose stdin stream closed (process exited) or was never opened; calling `shutdown` after the kernel process already terminated and its stdin was released.","commonSituations":"Kernel process crashed mid-session so subsequent execute calls hit a closed stdin; double-shutdown (shutdown called twice, second call finds stdin null); executing before `start()` finished opening the streams.","solutions":["Ensure `start()` completed successfully before executing; check `isAlive()` first","Make shutdown idempotent in caller code — skip or try-catch shutdown when the kernel already exited","Restart the kernel (recreate the session) if the process died and stdin closed"],"exampleFix":"// before\nawait kernel.shutdown(); // may throw if already dead\n// after\nif (kernel.isAlive()) {\n  await kernel.shutdown().catch(() => undefined);\n}","handlingStrategy":"try-catch","validationCode":"if (!kernel.isAlive()) {\n  throw new Error(\"skip write: kernel stdin closed\"); // or restart first\n}","typeGuard":null,"tryCatchPattern":"try {\n  await kernel.shutdown();\n} catch (err) {\n  if (err instanceof Error && /stdin is not open/.test(err.message)) {\n    // kernel already gone; treat shutdown as done\n  }\n}","preventionTips":["Make shutdown idempotent in calling code (catch this error)","Don't execute on a kernel whose process exited — check isAlive() first","Track process exit so you never double-shutdown"],"tags":["kernel","stdin","ipc","process"],"backgroundTag":"stream-already-closed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}