t8y2/dbx · error
close %s: %w
Error message
close %s: %w
What it means
This panic wraps an error from process.close() after a measured startup iteration. close() performs the 'shutdown' RPC, closes stdin, then cmd.Wait(); the returned error is either the shutdown RPC failure or an abnormal child exit reported by Wait. In the measured path this aborts the whole benchmark because remaining iterations cannot trust the agent lifecycle.
Source
Thrown at agents/drivers/rabbitmq/bench/agent_compare.go:191
if err != nil {
panic(fmt.Errorf("start %s: %w", agent.Name, err))
}
handshakeStart := time.Now()
if _, err := process.call("handshake", map[string]any{}); err != nil {
process.kill()
panic(fmt.Errorf("handshake %s: %w", agent.Name, err))
}
handshakeDuration := time.Since(handshakeStart)
readySamples[agent.Name] = append(readySamples[agent.Name], milliseconds(readyDuration))
handshakeSamples[agent.Name] = append(
handshakeSamples[agent.Name],
milliseconds(readyDuration+handshakeDuration),
)
rssSamples[agent.Name] = append(rssSamples[agent.Name], readRSSKB(process.command.Process.Pid))
readyDurations[agent.Name] += readyDuration
handshakeDurations[agent.Name] += readyDuration + handshakeDuration
if err := process.close(); err != nil {
panic(fmt.Errorf("close %s: %w", agent.Name, err))
}
}
}
results := make([]benchmarkResult, 0, len(agents)*2)
for _, agent := range agents {
artifactBytes := fileSize(agent.ArtifactPath)
ready := summarize(agent.Name, "startup_ready", 0, readySamples[agent.Name], readyDurations[agent.Name], 0)
ready.ReadyRSSKB = medianInt64(rssSamples[agent.Name])
ready.ArtifactBytes = artifactBytes
results = append(results, ready)
withHandshake := summarize(
agent.Name,
"startup_handshake",
0,
handshakeSamples[agent.Name],
handshakeDurations[agent.Name],
0,View on GitHub (pinned to c0390bff16)
Solutions
- Check agent stderr for the teardown error and fix the agent's shutdown path to reply then exit 0.
- If the agent exits on stdin close by design, change close() to send shutdown best-effort and only fail on a Wait() error.
- Relax Wait() error handling: treat ordinary non-zero exits at shutdown as non-fatal (log only) since samples were already collected.
- Confirm nothing external (docker healthchecks, oom-killer, CI timeouts) kills children mid-run.
- Replace the panic with an error return so one bad close does not discard the entire run.
Example fix
// before
if err := process.close(); err != nil {
panic(fmt.Errorf("close %s: %w", agent.Name, err))
}
// after
if err := process.close(); err != nil {
log.Printf("close %s: %v", agent.Name, err)
} Defensive patterns
Strategy: fallback
Validate before calling
// Prefer graceful shutdown; only treat real Wait() failures as fatal
err := process.close()
var exitErr *exec.ExitError
if err != nil && !errors.As(err, &exitErr) {
log.Fatalf("unexpected close failure: %v", err)
}
// Non-zero exits at teardown are logged, not fatal, since samples were collected. Type guard
func isBenignCloseError(err error) bool {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return true
}
return err != nil && strings.Contains(err.Error(), "agent response unavailable")
} Try / catch
func safeClose(p *agentProcess, name string) {
defer func() { _ = recover() }()
if err := p.close(); err != nil {
log.Printf("close %s: %v", name, err)
}
}
// Fallback: if shutdown RPC failed, force-kill so no orphans remain.
// if err != nil { process.kill() } Prevention
- Always fall back to process.kill() when the graceful shutdown RPC fails, to avoid orphaned children.
- Treat non-zero teardown exits as warnings in a benchmark harness, not aborts.
- Reply-then-exit contract for the shutdown RPC should be covered by agent tests.
- Do not let a single close error discard all collected samples.
- Correlate close failures with the agent's stderr output for the root cause.
When it happens
Trigger: In benchmarkStartups measured loop: after sampling ready/handshake/RSS, process.close() fails — the shutdown request write/read fails ('agent response unavailable: ...' because the agent already exited), or Wait() returns 'exit status N' (non-zero) or 'signal: killed'.
Common situations: Agent exits non-zero during teardown (cleanup error, failed broker disconnect); agent treats stdin EOF as exit and never answers shutdown, making callError non-nil; oom-killer or timeout watchdog kills the JVM; agent version regression changed shutdown reply format so the harness errors on the response.
Related errors
- close startup warmup %s: %w
- warm up handshake %s: %w
- start %s: %w
- handshake %s: %w
- strings.Join(failures, "; ")
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/aba485614496d365.
Report an issue: GitHub.