{"record":{"id":"da96fbf4b385533c","repo":"sipeed/picoclaw","slug":"credential-credential-file-q-is-empty","errorCode":null,"errorMessage":"credential: credential file %q is empty","messagePattern":"credential: credential file %q is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/credential/credential.go","lineNumber":142,"sourceCode":"\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) {\n\t\treturn resolveEncrypted(raw)\n\t}\n\n\t// Plaintext credential — return unchanged.\n\treturn raw, nil\n}\n\n// resolveEncrypted decrypts an enc:// credential using PassphraseProvider.\nfunc resolveEncrypted(raw string) (string, error) {\n\tpassphrase := PassphraseProvider()\n\tif passphrase == \"\" {\n\t\treturn \"\", ErrPassphraseRequired","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/credential/credential.go#L124-L160","documentation":"Returned by Resolver.Resolve when the credential file was found and read successfully but its content is empty after TrimSpace. A file:// reference exists to hold a secret, so an empty (or whitespace-only) file is treated as a configuration defect rather than silently resolving to an empty credential — that would produce baffling downstream 401s. The path is included in the error.","triggerScenarios":"`touch config/openai.key` created a zero-byte placeholder and config references file://openai.key; file contains only whitespace/newlines; a provisioning step wrote the secret to the wrong file and left this one blank.","commonSituations":"Setup checklists that create empty placeholder files to fill in later; secret-injection scripts that echo an empty env var into the file (`echo $API_KEY > openai.key` with API_KEY unset); truncated writes from a crashed provisioner.","solutions":["Write the actual secret into the exact path shown in the error: `printf '%s' 'sk-...' > <path>`","If the file is a placeholder, remove it and use a real empty-string credential (omit the key) only if the provider genuinely needs no key","Check your provisioning script for `echo $VAR > file` with unset/empty VAR — make it fail loudly instead"],"exampleFix":"# before\n: > config/openai.key   # zero-byte placeholder\n\n# after\nprintf '%s' 'sk-...' > config/openai.key && chmod 600 config/openai.key","handlingStrategy":"validation","validationCode":"// Preflight: every file:// target must have non-blank content.\ndata, err := os.ReadFile(p)\nif err != nil { return err }\nif strings.TrimSpace(string(data)) == \"\" {\n\treturn fmt.Errorf(\"credential file %s is empty — fill it before boot\", p)\n}","typeGuard":null,"tryCatchPattern":"if _, err := resolver.Resolve(raw); err != nil {\n\tif strings.Contains(err.Error(), \"is empty\") {\n\t\t// provisioning wrote nothing; fail fast with the file path from the message\n\t}\n\treturn err\n}","preventionTips":["Make secret-injection scripts error on empty source values (set -u; test -n \"$VAR\")","Avoid touch-placeholder patterns for credential files","Add a boot-time preflight listing all empty credential files"],"tags":["go","credentials","config","file-scheme","setup"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}