sipeed/picoclaw · critical
start deltachat-rpc-server (%s): %w
Error message
start deltachat-rpc-server (%s): %w
What it means
cmd.Start() failed for the already-resolved binary path: a fork/exec-level error, not a missing binary (that is caught earlier by resolveServerPath). Typical wrapped causes: permission denied (no execute bit), exec format error (wrong architecture or non-ELF file), or a noexec-mounted filesystem.
Source
Thrown at pkg/channels/deltachat/rpc.go:72
// 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),
}
go c.readLoop()
return c, nil
}
// readLoop reads newline-delimited responses and dispatches them to waiters.
func (c *rpcClient) readLoop() {
reader := bufio.NewReader(c.stdout)
for {
line, err := reader.ReadBytes('\n')
if len(line) > 0 {View on GitHub (pinned to 49183d7e8d)
Solutions
- chmod +x the binary and confirm with `ls -l`
- Download the release matching the host architecture (uname -m)
- If on a noexec mount, move the binary to a normal exec path (e.g. /usr/local/bin)
- Or reinstall via cargo/package manager, which sets the exec bit correctly
Example fix
# before curl -L -o /opt/dc/deltachat-rpc-server <release-url> # no execute bit -> start fails # after curl -L -o /usr/local/bin/deltachat-rpc-server <release-url> chmod +x /usr/local/bin/deltachat-rpc-server
Defensive patterns
Strategy: validation
Validate before calling
// Verify the binary is an executable regular file on an exec-allowed mount
func executablePresent(p string) error {
info, err := os.Stat(p)
if err != nil {
return err
}
if info.IsDir() || info.Mode()&0111 == 0 {
return fmt.Errorf("%s is not executable (chmod +x)", p)
}
return nil
} Type guard
func isExecFailure(err error) bool {
return errors.Is(err, fs.ErrPermission) ||
strings.Contains(err.Error(), "exec format error")
} Try / catch
if err := ch.Start(ctx); err != nil {
var pathErr *fs.PathError
if errors.As(err, &pathErr) || strings.Contains(err.Error(), "exec format error") {
// binary problem: chmod +x, correct architecture, or move off a noexec mount
}
return err
} Prevention
- chmod +x release binaries in install scripts and verify with test -x
- Download the release matching uname -m
- Never place the binary on noexec-mounted volumes (/tmp, some bind mounts)
When it happens
Trigger: exec.Command(serverPath).Start() errors: chmod +x never run on a downloaded release, ARM64 binary on x86_64 (or vice versa), a Mac binary on Linux, or the binary living on a volume mounted noexec.
Common situations: Manually downloaded GitHub release assets without chmod; cross-architecture deployment; /tmp or bind-mounted volumes mounted with noexec.
Related errors
- deltachat: deltachat-rpc-server not found on PATH (set rpc_s
- deltachat: rpc_server_path %q not found
- deltachat rpc stdin: %w
- deltachat rpc stdout: %w
- start process hook %q: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/8f0d5942a1d150d6.
Report an issue: GitHub.