{"record":{"id":"9317d9ddb2462ff4","repo":"sipeed/picoclaw","slug":"credential-enc-passphrase-required","errorCode":null,"errorMessage":"credential: enc:// passphrase required","messagePattern":"credential: enc:// passphrase required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/credential/credential.go","lineNumber":63,"sourceCode":"const PassphraseEnvVar = \"PICOCLAW_KEY_PASSPHRASE\"\n\n// PassphraseProvider is the function used to retrieve the passphrase for enc://\n// credential decryption. It defaults to reading PICOCLAW_KEY_PASSPHRASE from the\n// process environment. Replace it at startup to use a different source, such as\n// an in-memory SecureStore, so that all LoadConfig() calls everywhere share the\n// same passphrase source without needing os.Environ.\n//\n// Example (launcher main.go):\n//\n//\tcredential.PassphraseProvider = apiHandler.passphraseStore.Get\nvar PassphraseProvider func() string = func() string {\n\treturn os.Getenv(PassphraseEnvVar)\n}\n\n// ErrPassphraseRequired is returned when an enc:// credential is encountered but\n// no passphrase is available from PassphraseProvider. Callers can detect this\n// with errors.Is to distinguish a missing-passphrase condition from other errors.\nvar ErrPassphraseRequired = errors.New(\"credential: enc:// passphrase required\")\n\n// ErrDecryptionFailed is returned when an enc:// credential cannot be decrypted,\n// indicating a wrong passphrase or SSH key. Callers can detect this with errors.Is.\nvar ErrDecryptionFailed = errors.New(\"credential: enc:// decryption failed (wrong passphrase or SSH key?)\")\n\n// SSHKeyPathEnvVar is the environment variable that specifies the path to the\n// SSH private key used for enc:// credential encryption and decryption.\nconst SSHKeyPathEnvVar = \"PICOCLAW_SSH_KEY_PATH\"\n\n// picoclawHome is a package-local copy of config.EnvHome. It is kept here to\n// avoid a circular import between pkg/credential and pkg/config.\nconst picoclawHome = \"PICOCLAW_HOME\"\n\nconst (\n\tFileScheme = \"file://\"\n\tEncScheme  = \"enc://\"\n\n\thkdfInfo = \"picoclaw-credential-v1\"","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/credential/credential.go#L45-L81","documentation":"Returned by credential.Resolve when a model_list api_key uses the \"enc://\" scheme but the passphrase source is empty. resolveEncrypted calls PassphraseProvider(), which defaults to reading PICOCLAW_KEY_PASSPHRASE from the environment; an empty string aborts before any crypto runs. Detect it with errors.Is(err, credential.ErrPassphraseRequired).","triggerScenarios":"Resolver.Resolve(\"enc://<base64>\") (directly or via LoadConfig) while PICOCLAW_KEY_PASSPHRASE is unset or empty, and credential.PassphraseProvider has not been replaced with a non-empty source.","commonSituations":"Config encrypted on machine A (or via the picoclaw CLI) then run on machine B, in systemd/docker/CI where the env var was never exported; an embedded launcher replaced PassphraseProvider with a SecureStore that returns \"\" until the user unlocks it.","solutions":["Export PICOCLAW_KEY_PASSPHRASE with the same passphrase used when the key was encrypted: export PICOCLAW_KEY_PASSPHRASE='...'","If embedding picoclaw, set credential.PassphraseProvider = passphraseStore.Get in main() before any LoadConfig call","If the environment cannot carry secrets, replace the enc:// value with a file://<name>.key reference next to the config or re-embed the plaintext key","Double-check the api_key really starts with \"enc://\" and is not a stray literal that accidentally matches the scheme"],"exampleFix":"# before\n$ picoclaw gateway   # config uses enc:// key, no env set\n\n# after\n$ export PICOCLAW_KEY_PASSPHRASE='my-passphrase'\n$ picoclaw gateway","handlingStrategy":"validation","validationCode":"// before LoadConfig, when any api_key may be enc://\nfunc hasEncCredential(cfg *config.Config) bool {\n    for _, m := range cfg.ModelList {\n        if strings.HasPrefix(m.APIKey, credential.EncScheme) { return true }\n    }\n    return false\n}\nif hasEncCredential(cfg) && credential.PassphraseProvider() == \"\" {\n    return fmt.Errorf(\"PICOCLAW_KEY_PASSPHRASE required for enc:// credentials\")\n}","typeGuard":"func isPassphraseRequired(err error) bool {\n    return errors.Is(err, credential.ErrPassphraseRequired)\n}","tryCatchPattern":"val, err := resolver.Resolve(raw)\nif err != nil {\n    if errors.Is(err, credential.ErrPassphraseRequired) {\n        // prompt for / load the passphrase, then retry Resolve once\n    }\n    return err\n}","preventionTips":["Export PICOCLAW_KEY_PASSPHRASE in every environment that starts the gateway (shell profile, systemd unit, docker env)","Embedders: assign credential.PassphraseProvider once in main() before any LoadConfig","Fail fast at startup with a config pre-check instead of at first credential use"],"tags":["credentials","configuration","security","environment"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}