{"record":{"id":"77d0db6a7b2cb7ee","repo":"charmbracelet/crush","slug":"session-id-missing-from-context","errorCode":null,"errorMessage":"session id missing from context","messagePattern":"session id missing from context","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/agent_tool.go","lineNumber":50,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tagent, err := c.buildAgent(ctx, prompt, agentCfg, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fantasy.NewParallelAgentTool(\n\t\tAgentToolName,\n\t\tagentToolDescription,\n\t\tfunc(ctx context.Context, params AgentParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {\n\t\t\tif params.Prompt == \"\" {\n\t\t\t\treturn fantasy.NewTextErrorResponse(\"prompt is required\"), nil\n\t\t\t}\n\n\t\t\tsessionID := tools.GetSessionFromContext(ctx)\n\t\t\tif sessionID == \"\" {\n\t\t\t\treturn fantasy.ToolResponse{}, errors.New(\"session id missing from context\")\n\t\t\t}\n\n\t\t\tagentMessageID := tools.GetMessageFromContext(ctx)\n\t\t\tif agentMessageID == \"\" {\n\t\t\t\treturn fantasy.ToolResponse{}, errors.New(\"agent message id missing from context\")\n\t\t\t}\n\n\t\t\treturn c.runSubAgent(ctx, subAgentParams{\n\t\t\t\tAgent:          agent,\n\t\t\t\tSessionID:      sessionID,\n\t\t\t\tAgentMessageID: agentMessageID,\n\t\t\t\tToolCallID:     call.ID,\n\t\t\t\tPrompt:         params.Prompt,\n\t\t\t\tSessionTitle:   \"New Agent Session\",\n\t\t\t})\n\t\t},\n\t), nil\n}","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/agent_tool.go#L32-L68","documentation":"The 'agent' tool's Execute handler reads the current session id from the context via tools.GetSessionFromContext(ctx). Sub-agent runs must be attributed to a parent session for persistence and pub/sub; if the context carries no session id the tool cannot proceed and returns this error.","triggerScenarios":"Executing the agent tool with a context that was never populated by tools.WithSession (empty return from GetSessionFromContext) — e.g. invoking the tool directly in tests or via MCP plumbing without the session context decorators the normal agent loop installs.","commonSituations":"Custom tool runners or tests that call tool.Execute with a bare context.Background(); third-party integrations invoking tools outside the coordinator's dispatch path.","solutions":["Invoke the agent tool through the standard coordinator/agent loop, which sets the session context value.","In custom callers, wrap the context with tools.WithSessionContext(ctx, sessionID) before Execute.","Fix tests to use the same context helpers the production path uses."],"exampleFix":"// before\nresp, err := tool.Execute(ctx, params) // ctx has no session\n// after\nctx = tools.WithSessionContext(ctx, sessionID)\nresp, err := tool.Execute(ctx, params)","handlingStrategy":"validation","validationCode":"if tools.GetSessionFromContext(ctx) == \"\" {\n    return errors.New(\"session context required before executing agent tool\")\n}","typeGuard":null,"tryCatchPattern":"resp, err := tool.Execute(ctx, params)\nif err != nil && strings.Contains(err.Error(), \"session id missing from context\") {\n    ctx = tools.WithSessionContext(ctx, sessionID)\n    resp, err = tool.Execute(ctx, params)\n}","preventionTips":["Always execute agent tools through the coordinator's dispatch path.","Create a shared test helper that builds a fully populated tool context (session + message).","Never call tool.Execute with a bare context.Background() in integrations."],"tags":["context","session","tool-execution"],"backgroundTag":"missing-context-value","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}