{"record":{"id":"aba485614496d365","repo":"t8y2/dbx","slug":"close-s-w","errorCode":null,"errorMessage":"close %s: %w","messagePattern":"close (.+?): %w","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/rabbitmq/bench/agent_compare.go","lineNumber":191,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\tpanic(fmt.Errorf(\"start %s: %w\", agent.Name, err))\n\t\t\t}\n\t\t\thandshakeStart := time.Now()\n\t\t\tif _, err := process.call(\"handshake\", map[string]any{}); err != nil {\n\t\t\t\tprocess.kill()\n\t\t\t\tpanic(fmt.Errorf(\"handshake %s: %w\", agent.Name, err))\n\t\t\t}\n\t\t\thandshakeDuration := time.Since(handshakeStart)\n\t\t\treadySamples[agent.Name] = append(readySamples[agent.Name], milliseconds(readyDuration))\n\t\t\thandshakeSamples[agent.Name] = append(\n\t\t\t\thandshakeSamples[agent.Name],\n\t\t\t\tmilliseconds(readyDuration+handshakeDuration),\n\t\t\t)\n\t\t\trssSamples[agent.Name] = append(rssSamples[agent.Name], readRSSKB(process.command.Process.Pid))\n\t\t\treadyDurations[agent.Name] += readyDuration\n\t\t\thandshakeDurations[agent.Name] += readyDuration + handshakeDuration\n\t\t\tif err := process.close(); err != nil {\n\t\t\t\tpanic(fmt.Errorf(\"close %s: %w\", agent.Name, err))\n\t\t\t}\n\t\t}\n\t}\n\n\tresults := make([]benchmarkResult, 0, len(agents)*2)\n\tfor _, agent := range agents {\n\t\tartifactBytes := fileSize(agent.ArtifactPath)\n\t\tready := summarize(agent.Name, \"startup_ready\", 0, readySamples[agent.Name], readyDurations[agent.Name], 0)\n\t\tready.ReadyRSSKB = medianInt64(rssSamples[agent.Name])\n\t\tready.ArtifactBytes = artifactBytes\n\t\tresults = append(results, ready)\n\t\twithHandshake := summarize(\n\t\t\tagent.Name,\n\t\t\t\"startup_handshake\",\n\t\t\t0,\n\t\t\thandshakeSamples[agent.Name],\n\t\t\thandshakeDurations[agent.Name],\n\t\t\t0,","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/rabbitmq/bench/agent_compare.go#L173-L209","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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."],"exampleFix":"// before\nif err := process.close(); err != nil {\n\tpanic(fmt.Errorf(\"close %s: %w\", agent.Name, err))\n}\n// after\nif err := process.close(); err != nil {\n\tlog.Printf(\"close %s: %v\", agent.Name, err)\n}","handlingStrategy":"fallback","validationCode":"// Prefer graceful shutdown; only treat real Wait() failures as fatal\nerr := process.close()\nvar exitErr *exec.ExitError\nif err != nil && !errors.As(err, &exitErr) {\n\tlog.Fatalf(\"unexpected close failure: %v\", err)\n}\n// Non-zero exits at teardown are logged, not fatal, since samples were collected.","typeGuard":"func isBenignCloseError(err error) bool {\n\tvar exitErr *exec.ExitError\n\tif errors.As(err, &exitErr) {\n\t\treturn true\n\t}\n\treturn err != nil && strings.Contains(err.Error(), \"agent response unavailable\")\n}","tryCatchPattern":"func safeClose(p *agentProcess, name string) {\n\tdefer func() { _ = recover() }()\n\tif err := p.close(); err != nil {\n\t\tlog.Printf(\"close %s: %v\", name, err)\n\t}\n}\n// Fallback: if shutdown RPC failed, force-kill so no orphans remain.\n// if err != nil { process.kill() }","preventionTips":["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."],"tags":["go","subprocess","shutdown","wait","panic"],"backgroundTag":"process-shutdown-failure","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}