{"record":{"id":"1d729d83af930498","repo":"sipeed/picoclaw","slug":"deltachat-rpc-stdin-w","errorCode":null,"errorMessage":"deltachat rpc stdin: %w","messagePattern":"deltachat rpc stdin: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/deltachat/rpc.go","lineNumber":62,"sourceCode":"\tcmd    *exec.Cmd\n\tstdin  io.WriteCloser\n\tstdout io.ReadCloser\n\n\tmu      sync.Mutex\n\tnextID  uint64\n\tpending map[uint64]chan rpcResponse\n\tclosed  bool\n}\n\n// startRPC spawns the RPC server with DC_ACCOUNTS_PATH pointing at dataDir and\n// begins the background read loop.\nfunc startRPC(serverPath, dataDir string) (*rpcClient, error) {\n\tcmd := exec.Command(serverPath)\n\tcmd.Env = append(cmd.Environ(), \"DC_ACCOUNTS_PATH=\"+dataDir)\n\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"deltachat rpc stdin: %w\", err)\n\t}\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"deltachat rpc stdout: %w\", err)\n\t}\n\t// Let the server's logs flow to our stderr for easy diagnostics.\n\tcmd.Stderr = logWriter{}\n\n\tif err := cmd.Start(); err != nil {\n\t\treturn nil, fmt.Errorf(\"start deltachat-rpc-server (%s): %w\", serverPath, err)\n\t}\n\n\tc := &rpcClient{\n\t\tcmd:     cmd,\n\t\tstdin:   stdin,\n\t\tstdout:  stdout,\n\t\tpending: make(map[uint64]chan rpcResponse),\n\t}","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/deltachat/rpc.go#L44-L80","documentation":"cmd.StdinPipe() failed while wiring up the JSON-RPC transport: the OS refused to create the pipe that carries newline-delimited requests to deltachat-rpc-server. Happens before the child is spawned, so no process is left behind.","triggerScenarios":"Pipe(2) fails, typically fd exhaustion (EMFILE) from hitting the process's open-file limit (ulimit -n).","commonSituations":"Long-running PicoClaw leaking file descriptors; tight container/systemd LimitNOFILE; heavy concurrent channel churn.","solutions":["Raise the open-file limit (ulimit -n / LimitNOFILE=) for the PicoClaw process","Find and fix fd leaks (lsof on the running pid)","Restart the process to release fds and retry"],"exampleFix":"# systemd unit\n# before\n# (default LimitNOFILE=1024)\n\n# after\n[Service]\nLimitNOFILE=65536","handlingStrategy":"try-catch","validationCode":"// Cheap fd headroom check before spawning the RPC child\nfunc fdHeadroom() bool {\n    var r syscall.Rlimit\n    if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &r); err != nil {\n        return true\n    }\n    return r.Cur < r.Max // rough signal; low Cur with leaks is the risk\n}","typeGuard":null,"tryCatchPattern":"if err != nil { // returned from startRPC\n    if strings.Contains(err.Error(), \"rpc stdin\") {\n        // OS-level pipe failure: check ulimit -n / fd leaks, then restart\n    }\n    return err\n}","preventionTips":["Set LimitNOFILE generously for long-running PicoClaw processes","Periodically audit open fds (lsof) in long-lived deployments"],"tags":["deltachat","rpc","os","resources","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}