sipeed/picoclaw · error

credential: enc:// decryption failed (wrong passphrase or SS

Error message

credential: enc:// decryption failed (wrong passphrase or SSH key?)

What it means

Returned when AES-256-GCM authentication fails while decrypting an enc:// credential (gcm.Open error, wrapped as ErrDecryptionFailed). The key is derived via HKDF-SHA256 from ikm=HMAC-SHA256(SHA256(sshKeyBytes), passphrase), so a wrong passphrase OR a different SSH key file both produce a different key and the same failure. Corrupted/truncated ciphertext fails the same way.

Source

Thrown at pkg/credential/credential.go:67

// 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"
	saltLen  = 16
	nonceLen = 12
	keyLen   = 32
)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Re-enter the exact passphrase used at encryption time into PICOCLAW_KEY_PASSPHRASE
  2. Restore or point to the original SSH key: export PICOCLAW_SSH_KEY_PATH=/path/to/key_used_when_encrypting (key bytes are part of key derivation)
  3. If the original key material is gone, re-encrypt the credential with credential.Encrypt(currentPassphrase, currentKeyPath, plaintext) and update the config
  4. Verify the enc:// blob is intact: single line, valid base64, no truncation from YAML quoting or editor reflow

Example fix

// before: key regenerated, old enc:// value no longer decrypts
// after: re-encrypt with current key material
enc, err := credential.Encrypt(passphrase, "/home/user/.ssh/picoclaw_ed25519.key", "sk-abc123")
// write enc into model_list api_key
Defensive patterns

Strategy: try-catch

Type guard

func isDecryptionFailed(err error) bool {
    return errors.Is(err, credential.ErrDecryptionFailed)
}

Try / catch

val, err := resolver.Resolve(raw)
if err != nil {
    if errors.Is(err, credential.ErrDecryptionFailed) {
        // wrong passphrase OR wrong SSH key: surface a 're-enter passphrase / restore key' prompt;
        // do NOT brute-force retry in a loop
    }
    return err
}

Prevention

When it happens

Trigger: resolveEncrypted on a valid enc:// payload with: (a) PICOCLAW_KEY_PASSPHRASE differing from the encrypt-time passphrase, (b) the SSH key resolved via PICOCLAW_SSH_KEY_PATH or ~/.ssh/picoclaw_ed25519.key differing (by bytes) from the key used by Encrypt, or (c) a base64 blob mangled in transit (quotes/reflow in YAML, truncated line).

Common situations: Rotated or regenerated ~/.ssh/picoclaw_ed25519.key after encrypting keys; typos in the passphrase; copying config between hosts with different default keys; the key file in PICOCLAW_SSH_KEY_PATH pointing at a different machine's key.

Related errors


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