{"record":{"id":"9b758afad228212f","repo":"sipeed/picoclaw","slug":"credential-file-reference-has-no-filename","errorCode":null,"errorMessage":"credential: file:// reference has no filename","messagePattern":"credential: file:// reference has no filename","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/credential/credential.go","lineNumber":119,"sourceCode":"\t\t}\n\t}\n\treturn &Resolver{configDir: configDir, resolvedConfigDir: resolved}\n}\n\n// Resolve returns the actual credential value for raw:\n//\n//   - \"\"                → \"\" (no error; auth_method=oauth needs no key)\n//   - \"file://name.key\" → trimmed content of configDir/name.key\n//   - anything else     → raw unchanged (plaintext credential)\nfunc (r *Resolver) Resolve(raw string) (string, error) {\n\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)","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/credential/credential.go#L101-L137","documentation":"Returned by Resolver.Resolve when a credential value starts with `file://` but the remainder is empty or only whitespace. The file scheme promises an indirection (\"read the secret from this file in the config dir\"), so a bare scheme with no filename is a malformed reference, not a lookup miss. Empty-string credentials short-circuit earlier, so this only fires for literal `file://` (or `file://   `).","triggerScenarios":"Config contains `api_key: file://` or `api_key: \"file:// \"`. strings.TrimPrefix strips the scheme, TrimSpace removes whitespace, and the empty remainder triggers the error.","commonSituations":"Template placeholders left unfilled (`api_key: file://${KEY_FILE}`), users writing file:// intending \"use some file\" without knowing the filename is required, or trailing- edit accidents deleting the filename.","solutions":["Complete the reference: `file://name.key` where name.key lives in the config dir","If you meant an empty credential (e.g. oauth), use an empty string instead of file://","Check templating/envsubst output for unfilled ${...} placeholders that leave file:// bare"],"exampleFix":"# before\napi_key: file://\n\n# after\napi_key: file://openai.key","handlingStrategy":"validation","validationCode":"// Validate file:// references before resolving.\nfunc fileRefHasName(raw string) error {\n\tif strings.HasPrefix(raw, \"file://\") {\n\t\tif strings.TrimSpace(strings.TrimPrefix(raw, \"file://\")) == \"\" {\n\t\t\treturn fmt.Errorf(\"file:// reference %q has no filename\", raw)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":"func isCompleteFileRef(raw string) bool {\n\tif !strings.HasPrefix(raw, \"file://\") { return true }\n\treturn strings.TrimSpace(strings.TrimPrefix(raw, \"file://\")) != \"\"\n}","tryCatchPattern":"val, err := resolver.Resolve(raw)\nif err != nil {\n\tif strings.Contains(err.Error(), \"no filename\") {\n\t\t// config authoring bug — fail fast with config file/line context\n\t}\n\treturn \"\", err\n}","preventionTips":["Fail template rendering when ${...} placeholders are unfilled","Lint configs for bare `file://` values","Remember empty credential = empty string, not file://"],"tags":["go","credentials","config","validation","file-scheme"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}