sipeed/picoclaw · error

deltachat rpc stdin: %w

Error message

deltachat rpc stdin: %w

What it means

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.

Source

Thrown at pkg/channels/deltachat/rpc.go:62

	cmd    *exec.Cmd
	stdin  io.WriteCloser
	stdout io.ReadCloser

	mu      sync.Mutex
	nextID  uint64
	pending map[uint64]chan rpcResponse
	closed  bool
}

// startRPC spawns the RPC server with DC_ACCOUNTS_PATH pointing at dataDir and
// begins the background read loop.
func startRPC(serverPath, dataDir string) (*rpcClient, error) {
	cmd := exec.Command(serverPath)
	cmd.Env = append(cmd.Environ(), "DC_ACCOUNTS_PATH="+dataDir)

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, fmt.Errorf("deltachat rpc stdin: %w", err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("deltachat rpc stdout: %w", err)
	}
	// Let the server's logs flow to our stderr for easy diagnostics.
	cmd.Stderr = logWriter{}

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("start deltachat-rpc-server (%s): %w", serverPath, err)
	}

	c := &rpcClient{
		cmd:     cmd,
		stdin:   stdin,
		stdout:  stdout,
		pending: make(map[uint64]chan rpcResponse),
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Raise the open-file limit (ulimit -n / LimitNOFILE=) for the PicoClaw process
  2. Find and fix fd leaks (lsof on the running pid)
  3. Restart the process to release fds and retry

Example fix

# systemd unit
# before
# (default LimitNOFILE=1024)

# after
[Service]
LimitNOFILE=65536
Defensive patterns

Strategy: try-catch

Validate before calling

// Cheap fd headroom check before spawning the RPC child
func fdHeadroom() bool {
    var r syscall.Rlimit
    if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &r); err != nil {
        return true
    }
    return r.Cur < r.Max // rough signal; low Cur with leaks is the risk
}

Try / catch

if err != nil { // returned from startRPC
    if strings.Contains(err.Error(), "rpc stdin") {
        // OS-level pipe failure: check ulimit -n / fd leaks, then restart
    }
    return err
}

Prevention

When it happens

Trigger: Pipe(2) fails, typically fd exhaustion (EMFILE) from hitting the process's open-file limit (ulimit -n).

Common situations: Long-running PicoClaw leaking file descriptors; tight container/systemd LimitNOFILE; heavy concurrent channel churn.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/1d729d83af930498. Report an issue: GitHub.