{"record":{"id":"bfcc35ebd0ea3e5d","repo":"chenhg5/cc-connect","slug":"write-rpc-message-w","errorCode":null,"errorMessage":"write rpc message: %w","messagePattern":"write rpc message: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/codex/session.go","lineNumber":782,"sourceCode":"\t}\n}\n\nfunc rpcNotifyOverIO(stdin io.Writer, method string, params any) error {\n\tpayload := map[string]any{\n\t\t\"jsonrpc\": \"2.0\",\n\t\t\"method\":  method,\n\t\t\"params\":  params,\n\t}\n\treturn writeRPCMessage(stdin, payload)\n}\n\nfunc writeRPCMessage(w io.Writer, payload any) error {\n\tb, err := json.Marshal(payload)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"encode rpc message: %w\", err)\n\t}\n\tif _, err := w.Write(append(b, '\\n')); err != nil {\n\t\treturn fmt.Errorf(\"write rpc message: %w\", err)\n\t}\n\treturn nil\n}\n\n// RespondPermission is a no-op for Codex — permissions are handled via CLI flags.\nfunc (cs *codexSession) RespondPermission(_ string, _ core.PermissionResult) error {\n\treturn nil\n}\n\nfunc (cs *codexSession) Events() <-chan core.Event {\n\treturn cs.events\n}\n\nfunc (cs *codexSession) CurrentSessionID() string {\n\tv, _ := cs.threadID.Load().(string)\n\treturn v\n}\n","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/session.go#L764-L800","documentation":"writeRPCMessage failed at the w.Write step: after successfully marshaling the JSON-RPC payload, writing the newline-delimited bytes to the codex app-server's stdin pipe failed, returning a wrapped \"write rpc message\" error. This means the pipe to the child process is broken — typically because the app-server process has exited or its stdin was closed.","triggerScenarios":"w.Write(append(b, '\\n')) returns a non-nil error (EPIPE/broken pipe, or io.ErrClosedPipe) inside writeRPCMessage when rpcRequestOverIO or rpcNotifyOverIO sends a message to the app-server.","commonSituations":"The codex app-server crashed or exited mid-session and a subsequent RPC is written to its dead stdin; the probe process from loadCodexRuntimeConfig was killed by its deferred cleanup while a write was in flight; concurrent sessions closed the pipe.","solutions":["Check whether the codex process is still alive (`ps aux | grep codex`); if it died, find its exit reason in logs/stderr and restart the session.","Update the Codex CLI if it crashes during initialization — an early crash surfaces as a broken pipe on the next write.","Avoid racing the session teardown: ensure Stop/session cleanup completes before issuing further RPCs.","Retry the request with a fresh session; the old pipe cannot be revived.","Check ulimits and OOM-killer logs if the process is being killed under memory pressure."],"exampleFix":"// before: writing to a crashed app-server stdin\nerr := writeRPCMessage(stdin, payload) // broken pipe\n// after: verify process health before RPC, else recreate\nif cmd.ProcessState != nil && cmd.ProcessState.Exited() {\n    return nil, fmt.Errorf(\"codex app-server exited: %s\", cmd.ProcessState)\n}","handlingStrategy":"try-catch","validationCode":"// ensure the child process is still running before writing\nif cmd.Process == nil || (cmd.ProcessState != nil && cmd.ProcessState.Exited()) {\n    return fmt.Errorf(\"codex app-server no longer running\")\n}","typeGuard":"func isBrokenPipe(err error) bool {\n    return errors.Is(err, syscall.EPIPE) || errors.Is(err, io.ErrClosedPipe) || strings.Contains(err.Error(), \"write rpc message\")\n}","tryCatchPattern":"err := rpcRequestOverIO(stdin, stdout, ctx, method, params, &out)\nif isBrokenPipe(err) {\n    slog.Warn(\"app-server pipe broken; restarting process\")\n    // recreate the process/session and replay the request once\n}","preventionTips":["Detect early app-server exit (wait + stderr capture) before issuing RPCs","Serialize session teardown with in-flight RPCs (no writes after stdin.Close())","Monitor child process health and restart sessions proactively on exit","Check OOM-killer / ulimit logs when crashes recur under load"],"tags":["codex","json-rpc","broken-pipe","process-lifecycle"],"backgroundTag":"broken-pipe","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}