sipeed/picoclaw · error

start process hook: %w

Error message

start process hook: %w

What it means

Returned by NewProcessHook when isolation.Start(cmd) — the shared child-process isolation entry point that ultimately calls exec.Cmd.Start — cannot launch the hook command. This is the most common hook startup failure: the wrapped error tells you why (exec format, not found, permission, bad working dir, or sandbox denial).

Source

Thrown at pkg/agent/hook_process.go:130

	if len(opts.Env) > 0 {
		cmd.Env = append(os.Environ(), opts.Env...)
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, fmt.Errorf("create process hook stdin: %w", err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("create process hook stdout: %w", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, fmt.Errorf("create process hook stderr: %w", err)
	}
	// Route hook subprocess startup through the shared isolation entry point so
	// process hooks inherit the same isolation behavior as other child processes.
	if err := isolation.Start(cmd); err != nil {
		return nil, fmt.Errorf("start process hook: %w", err)
	}

	ph := &ProcessHook{
		name:         name,
		opts:         opts,
		cmd:          cmd,
		stdin:        stdin,
		observeKinds: newProcessHookObserveKinds(opts.ObserveKinds),
		pending:      make(map[uint64]chan processHookRPCMessage),
		done:         make(chan struct{}),
	}

	go ph.readLoop(stdout)
	go ph.readStderr(stderr)
	go ph.waitLoop()

	helloCtx := ctx
	if helloCtx == nil {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Verify the command exists and is executable from the host process's cwd and PATH: use an absolute path in the hook config
  2. chmod +x the hook script and ensure scripts start with a valid shebang (#!/usr/bin/env ...)
  3. If opts.Dir is set, confirm that directory exists and is accessible
  4. If the isolation layer is active, check its log/config for a denial and allowlist the hook binary

Example fix

# before
hooks:
  process:
    my-hook:
      command: ["./hooks/my-hook"]   # relative, depends on cwd

# after
hooks:
  process:
    my-hook:
      command: ["/opt/myapp/hooks/my-hook"]   # absolute, executable
Defensive patterns

Strategy: validation

Validate before calling

func validateHookCommand(spec config.ProcessHookConfig) error {
    if len(spec.Command) == 0 {
        return fmt.Errorf("hook command is required")
    }
    bin := spec.Command[0]
    path := bin
    if !filepath.IsAbs(path) {
        var err error
        path, err = exec.LookPath(bin)
        if err != nil {
            return fmt.Errorf("hook binary %q not found in PATH: %w", bin, err)
        }
    }
    info, err := os.Stat(path)
    if err != nil {
        return fmt.Errorf("hook binary %q inaccessible: %w", path, err)
    }
    if info.Mode()&0111 == 0 {
        return fmt.Errorf("hook binary %q is not executable", path)
    }
    if spec.Dir != "" {
        if _, err := os.Stat(spec.Dir); err != nil {
            return fmt.Errorf("hook dir %q invalid: %w", spec.Dir, err)
        }
    }
    return nil
}

Type guard

func isHookStartError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "start process hook:")
}

Try / catch

ph, err := NewProcessHook(ctx, name, opts)
if err != nil {
    if isHookStartError(err) {
        // surface exec.ErrNotFound / permission errors with the binary path
        return fmt.Errorf("failed to launch hook %q (%s): %w", name, opts.Command[0], err)
    }
    return err
}

Prevention

When it happens

Trigger: spec.Command[0] points to a binary that does not exist (exec: "...": executable file not found in $PATH), a file without the executable bit, a non-ELF script without a shebang, opts.Dir set to a nonexistent directory, or the isolation/sandbox layer refusing the spawn.

Common situations: Relative command path resolved against an unexpected cwd; forgetting chmod +x on a hook script; Windows line endings breaking a shebang; hook binary not shipped/installed on the deployment host; sandbox config (seccomp/landlock/container profile) blocking exec.

Related errors


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