{"record":{"id":"c0066f09418e9bcd","repo":"charmbracelet/crush","slug":"session-id-is-required-for-creating-a-new-file","errorCode":null,"errorMessage":"session ID is required for creating a new file","messagePattern":"session ID is required for creating a new file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/edit.go","lineNumber":125,"sourceCode":"func createNewFile(edit editContext, filePath, content string, call fantasy.ToolCall) (fantasy.ToolResponse, error) {\n\tfileInfo, err := os.Stat(filePath)\n\tif err == nil {\n\t\tif fileInfo.IsDir() {\n\t\t\treturn fantasy.NewTextErrorResponse(fmt.Sprintf(\"path is a directory, not a file: %s\", filePath)), nil\n\t\t}\n\t\treturn fantasy.NewTextErrorResponse(fmt.Sprintf(\"file already exists: %s\", filePath)), nil\n\t} else if !os.IsNotExist(err) {\n\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"failed to access file: %w\", err)\n\t}\n\n\tdir := filepath.Dir(filePath)\n\tif err = os.MkdirAll(dir, 0o755); err != nil {\n\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"failed to create parent directories: %w\", err)\n\t}\n\n\tsessionID := GetSessionFromContext(edit.ctx)\n\tif sessionID == \"\" {\n\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"session ID is required for creating a new file\")\n\t}\n\n\t_, additions, removals := diff.GenerateDiff(\n\t\t\"\",\n\t\tcontent,\n\t\tstrings.TrimPrefix(filePath, edit.workingDir),\n\t)\n\tp, err := edit.permissions.Request(\n\t\tedit.ctx,\n\t\tpermission.CreatePermissionRequest{\n\t\t\tSessionID:   sessionID,\n\t\t\tPath:        fsext.PathOrPrefix(filePath, edit.workingDir),\n\t\t\tToolCallID:  call.ID,\n\t\t\tToolName:    EditToolName,\n\t\t\tAction:      \"write\",\n\t\t\tDescription: fmt.Sprintf(\"Create file %s\", filePath),\n\t\t\tParams: EditPermissionsParams{\n\t\t\t\tFilePath:   filePath,","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/edit.go#L107-L143","documentation":"Like the download tool, createNewFile requires a session ID from the request context to attach to the permission request and file tracking for the newly created file. An empty session ID is an invariant violation and the tool refuses to create the file.","triggerScenarios":"The edit tool's createNewFile handler runs with a context lacking the session ID key — direct invocation in tests, a custom agent harness not setting the session, or context being replaced between agent setup and tool execution.","commonSituations":"Tests calling the tool handler directly without the session context; embedding edit in a custom pipeline that rebuilds the context; regressions in agent wiring after refactors.","solutions":["Run file creation through the standard agent path, which injects the session ID","In tests, add the session ID to the context before invoking the handler","Audit any code that constructs a fresh context.Context for tool calls and re-attach session values"],"exampleFix":"// before\nresp, _ := editTool.Handle(context.Background(), params, call)\n// after\nctx := WithSessionContext(context.Background(), sessionID)\nresp, _ := editTool.Handle(ctx, params, call)","handlingStrategy":"validation","validationCode":"if GetSessionFromContext(edit.ctx) == \"\" {\n    return nil, fmt.Errorf(\"edit tool requires a session-scoped context\")\n}","typeGuard":null,"tryCatchPattern":"resp, err := editTool.Handle(ctx, params, call)\nif err != nil && strings.Contains(err.Error(), \"session ID is required\") {\n    return fmt.Errorf(\"wiring bug: re-invoke via the agent so ctx carries the session ID\")\n}","preventionTips":["Create files through the standard agent path so the session context is injected","In tests, replicate the agent's context key setup for sessions","Never replace the context with context.Background() between agent setup and tool execution"],"tags":["session","context","edit","permissions"],"backgroundTag":"missing-session-context","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}