{"record":{"id":"c1e2dca04c6df8e3","repo":"sipeed/picoclaw","slug":"deltachat-rpc-write-s-w","errorCode":null,"errorMessage":"deltachat rpc write %s: %w","messagePattern":"deltachat rpc write (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/deltachat/rpc.go","lineNumber":147,"sourceCode":"\tid := c.nextID\n\tch := make(chan rpcResponse, 1)\n\tc.pending[id] = ch\n\tc.mu.Unlock()\n\n\treq := rpcRequest{JSONRPC: \"2.0\", ID: id, Method: method, Params: params}\n\tdata, err := json.Marshal(req)\n\tif err != nil {\n\t\tc.clearPending(id)\n\t\treturn nil, err\n\t}\n\tdata = append(data, '\\n')\n\n\tc.mu.Lock()\n\t_, err = c.stdin.Write(data)\n\tc.mu.Unlock()\n\tif err != nil {\n\t\tc.clearPending(id)\n\t\treturn nil, fmt.Errorf(\"deltachat rpc write %s: %w\", method, err)\n\t}\n\n\tselect {\n\tcase <-ctx.Done():\n\t\tc.clearPending(id)\n\t\treturn nil, ctx.Err()\n\tcase resp := <-ch:\n\t\tif resp.Error != nil {\n\t\t\treturn nil, resp.Error\n\t\t}\n\t\treturn resp.Result, nil\n\t}\n}\n\nfunc (c *rpcClient) clearPending(id uint64) {\n\tc.mu.Lock()\n\tdelete(c.pending, id)\n\tc.mu.Unlock()","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/deltachat/rpc.go#L129-L165","documentation":"Writing the newline-delimited JSON-RPC request to the deltachat-rpc-server's stdin pipe failed; %s is the RPC method name and the wrapped error is the pipe error (typically os.ErrClosed or EPIPE/broken pipe). It means the child died between the closed-flag check and the write, or the pipe was closed by close(). The pending request is cleaned up via clearPending before returning.","triggerScenarios":"c.stdin.Write(data) inside call() fails when: the child process exited after the c.closed check passed (race window), rpcClient.close() closed the stdin pipe concurrently, or the server crashed mid-session leaving a broken pipe.","commonSituations":"deltachat-rpc-server crash from a bad accounts DB, calling RPC methods concurrently with Stop/close, child killed by OOM or container resource limits, binary version mismatch causing immediate exit.","solutions":["Restart the channel/rpcClient to respawn the child process; the pipe cannot be repaired","Check the method name in the message to see which call hit the dead pipe, and the child's stderr logs for the crash cause","Verify deltachat-rpc-server version compatibility and that DC_ACCOUNTS_PATH data dir is writable","Ensure no Send/RPC path runs after Stop; serialize lifecycle transitions with the call mutex"],"exampleFix":"// before\nif _, err := c.stdin.Write(data); err != nil {\n    return nil, err // no method context, callers cannot tell what failed\n}\n\n// after (as implemented, plus caller-side respawn)\nif _, err := c.stdin.Write(data); err != nil {\n    c.clearPending(id)\n    return nil, fmt.Errorf(\"deltachat rpc write %s: %w\", method, err)\n}\n// caller: on errors.Is(err, os.ErrClosed) || errors.Is(err, syscall.EPIPE), rebuild via startRPC()","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isBrokenPipe(err error) bool {\n    return errors.Is(err, os.ErrClosed) || errors.Is(err, syscall.EPIPE)\n}","tryCatchPattern":"if _, err := rpc.call(ctx, \"send_text_message\", chatID, text); err != nil {\n    if isBrokenPipe(err) { // method name is in the message, cause in the chain\n        rpc, err = startRPC(serverPath, dataDir)\n        if err != nil {\n            return err\n        }\n        _, err = rpc.call(ctx, \"send_text_message\", chatID, text)\n    }\n    if err != nil {\n        return err\n    }\n}","preventionTips":["Serialize close() and call() behind the same mutex ownership so writes never race shutdown","Watch the child process and respawn proactively on exit instead of discovering via EPIPE","Keep deltachat-rpc-server version pinned and compatible with the expected RPC schema","Log the method name from the error message to identify which call hit the dead pipe"],"tags":["deltachat","rpc","pipe","epipe","process"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}