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

  1. chmod +x the binary and confirm with `ls -l`
  2. Download the release matching the host architecture (uname -m)
  3. If on a noexec mount, move the binary to a normal exec path (e.g. /usr/local/bin)
  4. 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

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


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