sipeed/picoclaw · error
create process hook stdout: %w
Error message
create process hook stdout: %w
What it means
Returned by NewProcessHook when exec.Cmd.StdoutPipe() fails while creating the pipe that carries the hook's JSON-RPC responses back. Like the stdin case, it is an OS pipe creation failure — almost always fd exhaustion (EMFILE/ENFILE) at the moment of the call.
Source
Thrown at pkg/agent/hook_process.go:121
}
func NewProcessHook(ctx context.Context, name string, opts ProcessHookOptions) (*ProcessHook, error) {
if len(opts.Command) == 0 {
return nil, fmt.Errorf("process hook command is required")
}
cmd := exec.Command(opts.Command[0], opts.Command[1:]...)
cmd.Dir = opts.Dir
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),View on GitHub (pinned to 49183d7e8d)
Solutions
- Raise the fd limit (ulimit -n / container nofile) so all three hook pipes can be created
- Close or reuse existing ProcessHook instances and other fd holders before mounting new hooks
- If it recurs, audit the process for leaked pipes with lsof and fix the leak
Defensive patterns
Strategy: try-catch
Type guard
func isStdoutPipeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "create process hook stdout:")
} Try / catch
ph, err := NewProcessHook(ctx, name, opts)
if err != nil {
if isStdoutPipeError(err) {
return fmt.Errorf("fd exhaustion while mounting hook %q: raise ulimit -n: %w", name, err)
}
return err
} Prevention
- Raise nofile limits in container specs proactively
- Reuse a single hook process instead of one per session
- Alert on fd usage trends before exhaustion hits
When it happens
Trigger: NewProcessHook invoked after stdin succeeded but the process fd table is full, so the second pipe allocation fails. Wraps the raw os error with the "create process hook stdout" context.
Common situations: Same class as the stdin failure: fd leaks in long-lived daemons, low container nofile limits, or mounting a large burst of process hooks concurrently.
Related errors
- create process hook stdin: %w
- create process hook stderr: %w
- session not found
- session already completed
- failed to load MCP servers: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/498758875fb7eebe.
Report an issue: GitHub.