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

  1. Read the agent's stderr (inherited to os.Stderr) for the crash output; fix the underlying agent crash (missing config, bad JAR, protocol mismatch).
  2. Verify JAVA_AGENT_JAR/GO_AGENT point to freshly built, runnable artifacts — run the agent manually and issue a handshake request over stdin.
  3. Rebuild both agents so their JSON-RPC handshake matches the harness format ({"id":n,"result":...} with matching ids).
  4. Check machine memory/ulimits; the benchmark spawns processes repeatedly and children can be OOM-killed.
  5. 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

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

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/ca45c4bc92048006. Report an issue: GitHub.