{"record":{"id":"d809b1eae27077eb","repo":"alibaba/open-code-review","slug":"read-tools-file-s-w","errorCode":null,"errorMessage":"read tools file %s: %w","messagePattern":"read tools file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/toolsconfig/toolsconfig.go","lineNumber":35,"sourceCode":"\tPlanTask   bool            `json:\"plan_task\"`\n\tMainTask   bool            `json:\"main_task\"`\n\tDefinition json.RawMessage `json:\"definition\"`\n}\n\n//go:embed tools.json\nvar defaultToolsJSON []byte\n\n// Load parses the tools config file. When path is empty, falls back to\n// the embedded default tools configuration.\nfunc Load(path string) ([]ToolConfigEntry, error) {\n\tvar data []byte\n\tvar err error\n\tif path == \"\" {\n\t\tdata = defaultToolsJSON\n\t} else {\n\t\tdata, err = os.ReadFile(path)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"read tools file %s: %w\", path, err)\n\t\t}\n\t}\n\tvar tools []ToolConfigEntry\n\tif err := json.Unmarshal(data, &tools); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal tools file: %w\", err)\n\t}\n\treturn tools, nil\n}\n\n// ToolDefsByPhase returns the parsed tool definitions filtered by phase.\n// planOnly=true returns only tools with plan_task:true.\n// planOnly=false returns only tools with main_task:true.\nfunc (t *ToolConfigEntry) ToolDefsByPhase(planOnly bool) (json.RawMessage, bool) {\n\tswitch {\n\tcase planOnly && t.PlanTask:\n\t\treturn t.Definition, true\n\tcase !planOnly && t.MainTask:\n\t\treturn t.Definition, true","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/config/toolsconfig/toolsconfig.go#L17-L53","documentation":"Load reads tool definitions either from the built-in defaultToolsJSON (path==\"\") or from the file at the given path; this error wraps os.ReadFile failures for a custom path. It means the tools config file could not be read at all — not that its contents are invalid.","triggerScenarios":"Calling Load(path) with a non-empty path where os.ReadFile fails: file does not exist, permission denied, path is a directory, or an I/O error.","commonSituations":"Passing a --tools flag with a wrong/relative path from a different working directory; the tools file was deleted or renamed; running under a service account lacking read permission.","solutions":["Verify the path exists and is a readable file (ls/ll the path), or pass an absolute path","Pass an empty path to fall back to the built-in default tools JSON","Fix filesystem permissions for the user running the tool"],"exampleFix":"// before\ntools, err := toolsconfig.Load(\"./tools.json\") // file missing\n// after\npath := \"./tools.json\"\nif _, err := os.Stat(path); err != nil { path = \"\" } // fall back to defaults\ntools, err := toolsconfig.Load(path)","handlingStrategy":"validation","validationCode":"info, err := os.Stat(path)\nif err != nil || info.IsDir() { return fmt.Errorf(\"tools file %s unavailable: %w\", path, err) }","typeGuard":null,"tryCatchPattern":"tools, err := toolsconfig.Load(path)\nif err != nil {\n    var pathErr *os.PathError\n    if errors.As(err, &pathErr) { tools, err = toolsconfig.Load(\"\") /* defaults */ }\n    if err != nil { return err }\n}","preventionTips":["Resolve tools-file paths to absolute at CLI flag parsing time","Fall back to the embedded default tools when no path is usable","Check file readability for the effective user in service/CI contexts"],"tags":["file-io","config","tools"],"backgroundTag":"file-not-found","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}