{"record":{"id":"a3def717253f9cc4","repo":"charmbracelet/crush","slug":"session-id-is-required-for-executing-shell-command","errorCode":null,"errorMessage":"session ID is required for executing shell command","messagePattern":"session ID is required for executing shell command","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/bash.go","lineNumber":225,"sourceCode":"\t\t\texecWorkingDir := cmp.Or(params.WorkingDir, workingDir)\n\n\t\t\tisSafeReadOnly := false\n\t\t\tcmdLower := strings.ToLower(params.Command)\n\n\t\t\tif !containsCommandChaining(params.Command) {\n\t\t\t\tfor _, safe := range safeCommands {\n\t\t\t\t\tif strings.HasPrefix(cmdLower, safe) {\n\t\t\t\t\t\tif len(cmdLower) == len(safe) || cmdLower[len(safe)] == ' ' || cmdLower[len(safe)] == '-' {\n\t\t\t\t\t\t\tisSafeReadOnly = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsessionID := GetSessionFromContext(ctx)\n\t\t\tif sessionID == \"\" {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"session ID is required for executing shell command\")\n\t\t\t}\n\t\t\tif !isSafeReadOnly {\n\t\t\t\tp, err := permissions.Request(\n\t\t\t\t\tctx,\n\t\t\t\t\tpermission.CreatePermissionRequest{\n\t\t\t\t\t\tSessionID:   sessionID,\n\t\t\t\t\t\tPath:        execWorkingDir,\n\t\t\t\t\t\tToolCallID:  call.ID,\n\t\t\t\t\t\tToolName:    BashToolName,\n\t\t\t\t\t\tAction:      \"execute\",\n\t\t\t\t\t\tDescription: fmt.Sprintf(\"Execute command: %s\", params.Command),\n\t\t\t\t\t\tParams:      BashPermissionsParams(params),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fantasy.ToolResponse{}, err\n\t\t\t\t}\n\t\t\t\tif !p {","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/bash.go#L207-L243","documentation":"The bash tool resolves the caller's session ID from the request context via GetSessionFromContext; if absent it refuses to execute the command, because the session ID is mandatory for the permission request (CreatePermissionRequest.SessionID) and for tracking the shell job. This is a guard against executing commands outside an agent session.","triggerScenarios":"Invoking the bash tool handler with a context that was never populated by SetSessionInContext — e.g. calling the fantasy tool function directly in tests or custom code without session middleware, or running the tool in a path that bypasses the agent's context setup.","commonSituations":"Unit/integration tests constructing the tool and calling it with context.Background(); embedding the tool into a custom runner that forgets to attach the session ID; third-party integrations calling tool.Run without the app's session plumbing.","solutions":["Wrap the call context with the session ID the agent sets (use the same SetSessionInContext/GetSessionFromContext helper the agent uses before invoking the tool)","If driving tools yourself, replicate internal/agent's per-request context setup so GetSessionFromContext returns a non-empty ID","In tests, inject a fake session ID into ctx before calling the tool handler"],"exampleFix":"// before\nresp, err := tool.Run(ctx, params)\n// after\nctx = agent.SetSessionInContext(ctx, \"my-session-id\")\nresp, err := tool.Run(ctx, params)","handlingStrategy":"validation","validationCode":"if agent.GetSessionFromContext(ctx) == \"\" {\n    return errors.New(\"call the bash tool within a session-scoped context\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always invoke tools through the agent/coordinator path that sets the session context","In tests, inject a session ID into ctx (same helper the agent uses) before calling the tool","When embedding tools in custom runners, replicate the app's context middleware"],"tags":["go","session","permissions","shell"],"backgroundTag":"missing-session-context","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}