t8y2/dbx · critical
warm up handshake %s: %w
Error message
warm up handshake %s: %w
What it means
This panic wraps any failure from the agent's JSON-RPC 'handshake' call during the startup warm-up phase of the benchmark. The benchmark spawns each agent as a subprocess, waits for its ready line, then writes a JSON-RPC handshake request to its stdin and reads the response. The call fails when the agent process dies, closes its stdout, returns malformed/unexpected JSON, or replies with a JSON-RPC error — so the wrapped error is usually 'agent response unavailable: <read error>' or an agent-side error message. It is thrown via panic because in this bench harness a warm-up failure means the environment is broken and the whole run is invalid.
Source
Thrown at agents/drivers/rabbitmq/bench/agent_compare.go:153
))
}
}
}
func benchmarkStartups(agents []agentSpec, warmups, iterations int) []benchmarkResult {
for warmup := 0; warmup < warmups; warmup++ {
order := agents
if warmup%2 == 1 {
order = []agentSpec{agents[1], agents[0]}
}
for _, agent := range order {
process, _, err := startAgent(agent.Command)
if err != nil {
panic(fmt.Errorf("warm up startup %s: %w", agent.Name, err))
}
if _, err := process.call("handshake", map[string]any{}); err != nil {
process.kill()
panic(fmt.Errorf("warm up handshake %s: %w", agent.Name, err))
}
if err := process.close(); err != nil {
panic(fmt.Errorf("close startup warmup %s: %w", agent.Name, err))
}
}
}
readySamples := map[string][]float64{}
handshakeSamples := map[string][]float64{}
rssSamples := map[string][]int64{}
readyDurations := map[string]time.Duration{}
handshakeDurations := map[string]time.Duration{}
for iteration := 0; iteration < iterations; iteration++ {
order := agents
if iteration%2 == 1 {
order = []agentSpec{agents[1], agents[0]}
}
for _, agent := range order {View on GitHub (pinned to c0390bff16)
Solutions
- Read the agent's stderr (inherited to os.Stderr) for the crash output; fix the underlying agent crash (missing config, bad JAR, protocol mismatch).
- Verify JAVA_AGENT_JAR/GO_AGENT point to freshly built, runnable artifacts — run the agent manually and issue a handshake request over stdin.
- Rebuild both agents so their JSON-RPC handshake matches the harness format ({"id":n,"result":...} with matching ids).
- Check machine memory/ulimits; the benchmark spawns processes repeatedly and children can be OOM-killed.
- As a code fix, replace panic with error return so partial results can be reported instead of aborting the run.
Example fix
// before
if _, err := process.call("handshake", map[string]any{}); err != nil {
process.kill()
panic(fmt.Errorf("warm up handshake %s: %w", agent.Name, err))
}
// after
if _, err := process.call("handshake", map[string]any{}); err != nil {
process.kill()
log.Printf("warm up handshake failed for %s: %v", agent.Name, err)
return nil, fmt.Errorf("warm up handshake %s: %w", agent.Name, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before running the benchmark, verify the agent artifact and binary:
jar := os.Getenv("JAVA_AGENT_JAR")
if _, err := os.Stat(jar); err != nil {
log.Fatalf("JAVA_AGENT_JAR not found: %v", err)
}
cmd := exec.Command("java", "-jar", jar)
cmd.Stdin = strings.NewReader("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"handshake\",\"params\":{}}\n")
out, err := cmd.Output()
if err != nil || !strings.Contains(string(out), "\"result\"") {
log.Fatalf("agent handshake smoke test failed: %v", err)
} Type guard
// Narrow the wrapped error to detect the read-EOF case from call()
func isResponseUnavailable(err error) bool {
return err != nil && strings.Contains(err.Error(), "agent response unavailable")
}
func isIDMismatch(err error) bool {
return err != nil && strings.Contains(err.Error(), "does not match request id")
} Try / catch
// Go uses recover instead of try/catch; scope it to the warm-up phase
func safeWarmupHandshake(p *agentProcess, name string) (err error) {
defer func() {
if r := recover(); r != nil {
p.kill()
err = fmt.Errorf("warm up handshake %s: %v", name, r)
}
}()
_, err = p.call("handshake", map[string]any{})
return err
} Prevention
- Smoke-test each agent binary with a single handshake before the benchmark loop.
- Ensure agents log to stderr only; stdout is reserved for JSON-RPC frames.
- Pin and rebuild agent artifacts together with the harness so protocols stay in sync.
- Set generous memory limits so the JVM is not OOM-killed during startup.
- Prefer returning errors over panic in library-style harness code.
When it happens
Trigger: During benchmarkStartups warm-ups: startAgent() succeeds but process.call("handshake") fails — the agent crashes after emitting its ready line, exits before answering (broken binary/JAR, JVM crash, OOM kill), writes nothing to stdout so reader.Scan() returns false ('agent response unavailable'), returns JSON whose id mismatches, or returns a JSON-RPC error object.
Common situations: JAVA_AGENT_JAR or GO_AGENT points to a stale/Corrupted artifact that starts but dies on first request; the JVM hits an OOM or UnsupportedClassVersionError mid-run; the agent was rebuilt with an incompatible handshake protocol (id or field mismatch); resource limits (ulimit, cgroup) kill the child during repeated rapid spawns.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/ca45c4bc92048006.
Report an issue: GitHub.