Skip to content

Commit 3a8201e

Browse files
committed
fix(hooks): preserve stderr and exitCode in hookResult error paths
When runHook encounters a timeout or handler error, it must preserve the handler's stderr output and set exitCode to -1 so aggregate can surface them correctly. The refactoring that embedded HandlerResult into hookResult inadvertently broke this by returning a zero-valued HandlerResult (empty stderr, exitCode=0) on error paths. This is critical for PreToolUse fail-closed semantics: when a hook fails to execute, aggregate reads r.Stderr to include in the denial message. Without this fix, the stderr is lost and users see only "PreToolUse hook failed to execute: <err>" without the underlying command's diagnostic output. The fix explicitly constructs HandlerResult{Stderr: res.Stderr, ExitCode: -1} in both error returns, matching the old behavior where runHook mutated r.stderr and r.exitCode before returning. Assisted-By: docker-agent
1 parent d27bb33 commit 3a8201e

1 file changed

Lines changed: 8 additions & 2 deletions

File tree

pkg/hooks/executor.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,16 @@ func (e *Executor) runHook(ctx context.Context, hook Hook, inputJSON []byte) hoo
218218
if errors.Is(ctxErr, context.DeadlineExceeded) {
219219
reason = fmt.Sprintf("timed out after %s", hook.GetTimeout())
220220
}
221-
return hookResult{err: fmt.Errorf("hook %q %s: %w", hook.Command, reason, ctxErr)}
221+
return hookResult{
222+
HandlerResult: HandlerResult{Stderr: res.Stderr, ExitCode: -1},
223+
err: fmt.Errorf("hook %q %s: %w", hook.Command, reason, ctxErr),
224+
}
222225
}
223226
if err != nil {
224-
return hookResult{err: err}
227+
return hookResult{
228+
HandlerResult: HandlerResult{Stderr: res.Stderr, ExitCode: -1},
229+
err: err,
230+
}
225231
}
226232

227233
// Fall back to the legacy "parse JSON from stdout" protocol.

0 commit comments

Comments
 (0)