{"record":{"id":"896d1547d5852a1e","repo":"charmbracelet/crush","slug":"session-id-is-required-for-downloading-files","errorCode":null,"errorMessage":"session ID is required for downloading files","messagePattern":"session ID is required for downloading files","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/download.go","lineNumber":87,"sourceCode":"\t\t\tif params.URL == \"\" {\n\t\t\t\treturn fantasy.NewTextErrorResponse(\"URL parameter is required\"), nil\n\t\t\t}\n\n\t\t\tif params.FilePath == \"\" {\n\t\t\t\treturn fantasy.NewTextErrorResponse(\"file_path parameter is required\"), nil\n\t\t\t}\n\n\t\t\tif !strings.HasPrefix(params.URL, \"http://\") && !strings.HasPrefix(params.URL, \"https://\") {\n\t\t\t\treturn fantasy.NewTextErrorResponse(\"URL must start with http:// or https://\"), nil\n\t\t\t}\n\n\t\t\tfilePath := filepathext.SmartJoin(workingDir, params.FilePath)\n\t\t\trelPath, _ := filepath.Rel(workingDir, filePath)\n\t\t\trelPath = filepath.ToSlash(cmp.Or(relPath, filePath))\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 downloading files\")\n\t\t\t}\n\n\t\t\tp, err := permissions.Request(\n\t\t\t\tctx,\n\t\t\t\tpermission.CreatePermissionRequest{\n\t\t\t\t\tSessionID:   sessionID,\n\t\t\t\t\tPath:        filePath,\n\t\t\t\t\tToolName:    DownloadToolName,\n\t\t\t\t\tAction:      \"download\",\n\t\t\t\t\tDescription: fmt.Sprintf(\"Download file from URL: %s to %s\", params.URL, filePath),\n\t\t\t\t\tParams:      DownloadPermissionsParams(params),\n\t\t\t\t},\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn fantasy.ToolResponse{}, err\n\t\t\t}\n\t\t\tif !p {\n\t\t\t\treturn NewPermissionDeniedResponse(), nil","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/download.go#L69-L105","documentation":"The download tool requires a session ID, obtained from the request context via GetSessionFromContext, to record the permission request for writing the downloaded file. If the context carries no session ID the tool refuses to run. This is an internal invariant: tool invocations are normally made by the agent, which always sets the session.","triggerScenarios":"The download tool's handler is invoked with a context.Context that has no session ID attached — e.g. calling the tool function directly in tests or scripts, or an agent wiring path that skips GetSessionFromContext's context key.","commonSituations":"Unit/integration tests invoking the tool handler without the session context key; embedding the tool in a custom agent that doesn't propagate the session ID; regressions after refactoring context plumbing.","solutions":["Invoke the tool through the normal agent/coordinator path which sets the session ID in the context","In tests, attach the session ID to the context the same way the agent does before calling the handler","Check that the context isn't being recreated/stripped between permission setup and tool execution"],"exampleFix":"// before\nresp, _ := tool.Handle(ctx, params, call)\n// after\nctx = WithSessionContext(ctx, \"my-session-id\")\nresp, _ := tool.Handle(ctx, params, call)","handlingStrategy":"validation","validationCode":"// Go (caller side)\nif GetSessionFromContext(ctx) == \"\" {\n    return nil, fmt.Errorf(\"attach a session ID to ctx before invoking the download tool\")\n}","typeGuard":null,"tryCatchPattern":"resp, err := tool.Handle(ctx, params, call)\nif err != nil && strings.Contains(err.Error(), \"session ID is required\") {\n    ctx = WithSessionContext(ctx, sessionID)\n    resp, err = tool.Handle(ctx, params, call)\n}","preventionTips":["Always invoke tools through the agent/coordinator, which sets the session context","In tests, mirror the agent's context setup (session key) before calling handlers","Don't discard the context between permission setup and tool execution"],"tags":["session","context","permissions"],"backgroundTag":"missing-session-context","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}