sipeed/picoclaw · error

credential: enc:// passphrase required

Error message

credential: enc:// passphrase required

What it means

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).

Source

Thrown at pkg/credential/credential.go:63

const PassphraseEnvVar = "PICOCLAW_KEY_PASSPHRASE"

// PassphraseProvider is the function used to retrieve the passphrase for enc://
// credential decryption. It defaults to reading PICOCLAW_KEY_PASSPHRASE from the
// process environment. Replace it at startup to use a different source, such as
// an in-memory SecureStore, so that all LoadConfig() calls everywhere share the
// same passphrase source without needing os.Environ.
//
// Example (launcher main.go):
//
//	credential.PassphraseProvider = apiHandler.passphraseStore.Get
var PassphraseProvider func() string = func() string {
	return os.Getenv(PassphraseEnvVar)
}

// ErrPassphraseRequired is returned when an enc:// credential is encountered but
// no passphrase is available from PassphraseProvider. Callers can detect this
// with errors.Is to distinguish a missing-passphrase condition from other errors.
var ErrPassphraseRequired = errors.New("credential: enc:// passphrase required")

// ErrDecryptionFailed is returned when an enc:// credential cannot be decrypted,
// indicating a wrong passphrase or SSH key. Callers can detect this with errors.Is.
var ErrDecryptionFailed = errors.New("credential: enc:// decryption failed (wrong passphrase or SSH key?)")

// SSHKeyPathEnvVar is the environment variable that specifies the path to the
// SSH private key used for enc:// credential encryption and decryption.
const SSHKeyPathEnvVar = "PICOCLAW_SSH_KEY_PATH"

// picoclawHome is a package-local copy of config.EnvHome. It is kept here to
// avoid a circular import between pkg/credential and pkg/config.
const picoclawHome = "PICOCLAW_HOME"

const (
	FileScheme = "file://"
	EncScheme  = "enc://"

	hkdfInfo = "picoclaw-credential-v1"

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Export PICOCLAW_KEY_PASSPHRASE with the same passphrase used when the key was encrypted: export PICOCLAW_KEY_PASSPHRASE='...'
  2. If embedding picoclaw, set credential.PassphraseProvider = passphraseStore.Get in main() before any LoadConfig call
  3. 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
  4. Double-check the api_key really starts with "enc://" and is not a stray literal that accidentally matches the scheme

Example fix

# before
$ picoclaw gateway   # config uses enc:// key, no env set

# after
$ export PICOCLAW_KEY_PASSPHRASE='my-passphrase'
$ picoclaw gateway
Defensive patterns

Strategy: validation

Validate before calling

// before LoadConfig, when any api_key may be enc://
func hasEncCredential(cfg *config.Config) bool {
    for _, m := range cfg.ModelList {
        if strings.HasPrefix(m.APIKey, credential.EncScheme) { return true }
    }
    return false
}
if hasEncCredential(cfg) && credential.PassphraseProvider() == "" {
    return fmt.Errorf("PICOCLAW_KEY_PASSPHRASE required for enc:// credentials")
}

Type guard

func isPassphraseRequired(err error) bool {
    return errors.Is(err, credential.ErrPassphraseRequired)
}

Try / catch

val, err := resolver.Resolve(raw)
if err != nil {
    if errors.Is(err, credential.ErrPassphraseRequired) {
        // prompt for / load the passphrase, then retry Resolve once
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/9317d9ddb2462ff4. Report an issue: GitHub.