{"record":{"id":"ce81b6b8f0fc1712","repo":"sipeed/picoclaw","slug":"credential-failed-to-resolve-credential-file-path","errorCode":null,"errorMessage":"credential: failed to resolve credential file path %q: %w","messagePattern":"credential: failed to resolve credential file path %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/credential/credential.go","lineNumber":130,"sourceCode":"\tif raw == \"\" {\n\t\treturn \"\", nil\n\t}\n\n\tif strings.HasPrefix(raw, FileScheme) {\n\t\tfileName := strings.TrimSpace(strings.TrimPrefix(raw, FileScheme))\n\t\tif fileName == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"credential: file:// reference has no filename\")\n\t\t}\n\n\t\tbaseDir := r.resolvedConfigDir\n\t\tif baseDir == \"\" {\n\t\t\tbaseDir = r.configDir\n\t\t}\n\t\tkeyPath := filepath.Join(baseDir, fileName)\n\t\t// Resolve symlinks before enforcing containment to prevent escaping via symlinks.\n\t\trealKeyPath, err := filepath.EvalSymlinks(keyPath)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"credential: failed to resolve credential file path %q: %w\", keyPath, err)\n\t\t}\n\t\tif !isWithinDir(realKeyPath, baseDir) {\n\t\t\treturn \"\", fmt.Errorf(\"credential: file:// path escapes config directory\")\n\t\t}\n\t\tdata, err := os.ReadFile(realKeyPath)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"credential: failed to read credential file %q: %w\", realKeyPath, err)\n\t\t}\n\n\t\tvalue := strings.TrimSpace(string(data))\n\t\tif value == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"credential: credential file %q is empty\", realKeyPath)\n\t\t}\n\n\t\treturn value, nil\n\t}\n\n\tif strings.HasPrefix(raw, EncScheme) {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/credential/credential.go#L112-L148","documentation":"Returned by Resolver.Resolve when filepath.EvalSymlinks fails on the joined path configDir/filename — overwhelmingly because the target file does not exist (ENOENT), but also for unreadable path components or I/O errors. Symlinks are resolved deliberately before containment enforcement, so a dangling symlink also lands here. The message includes the unresolved keyPath for debugging.","triggerScenarios":"`file://openai.key` in config but openai.key was never created in the resolved config dir; filename typo; file lives in a different directory than the resolver's baseDir; a symlink inside configDir pointing to a deleted target (EvalSymlinks returns ENOENT).","commonSituations":"Fresh clones/machines where credential files aren't checked in (they're secrets) and the setup step that writes them was skipped; running with a different --config dir or env var than the one where keys were placed; renaming a key file without updating the config.","solutions":["Check the path printed in the error: does <configDir>/<filename> exist? Create it with the secret content if missing","If the file exists elsewhere, either move/copy it into the config dir or fix the filename in the file:// reference","Fix or remove dangling symlinks inside the config dir (they fail EvalSymlinks even when the final name is right)","Verify which config dir the resolver uses (resolvedConfigDir overrides configDir when set) before assuming the location"],"exampleFix":"# before: config references file://openai.key, file absent\nls config/openai.key  # -> No such file\n\n# after: create the file with the secret\necho -n 'sk-...' > config/openai.key && chmod 600 config/openai.key","handlingStrategy":"validation","validationCode":"// Pre-check existence inside the resolver's base dir.\nbase := resolver.BaseDir() // resolvedConfigDir, falling back to configDir\np := filepath.Join(base, filename)\nif _, err := os.Stat(p); err != nil {\n\treturn fmt.Errorf(\"credential file %s missing: create it before boot\", p)\n}","typeGuard":null,"tryCatchPattern":"val, err := resolver.Resolve(raw)\nif err != nil {\n\tvar pathErr *fs.PathError\n\tif errors.As(err, &pathErr) && errors.Is(pathErr.Err, fs.ErrNotExist) {\n\t\t// missing/dangling file: actionable message with the printed keyPath\n\t}\n\treturn \"\", err\n}","preventionTips":["Add a startup preflight that stats every file:// target and lists all missing ones at once","Keep credential filenames stable; update config when renaming","Include credential file creation in setup docs/scripts for fresh machines"],"tags":["go","credentials","filesystem","file-scheme","setup"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}