zeroclaw-labs/zeroclaw · error

Encrypted value too short (missing nonce)

Error message

Encrypted value too short (missing nonce)

What it means

decrypt_chacha20 hex-decodes the stored secret and requires strictly more than NONCE_LEN (12) bytes: a 12-byte ChaCha20-Poly1305 nonce plus at least some ciphertext/tag material. A value of 12 bytes or fewer cannot contain a nonce, so it is rejected as corrupt or truncated before any cryptographic operation.

Source

Thrown at crates/zeroclaw-config/src/secrets.rs:330

        } else if is_onepassword_ref(value) {
            let plaintext = resolve_onepassword_ref(value)?;
            Ok((plaintext, None))
        } else {
            // Plaintext — no migration needed
            Ok((value.to_string(), None))
        }
    }

    /// Check if a value uses the legacy `enc:` format that should be migrated.
    pub fn needs_migration(value: &str) -> bool {
        value.starts_with("enc:")
    }

    /// Decrypt using ChaCha20-Poly1305 (current secure format).
    fn decrypt_chacha20(&self, hex_str: &str) -> Result<String> {
        let blob =
            hex_decode(hex_str).context("Failed to decode encrypted secret (corrupt hex)")?;
        anyhow::ensure!(
            blob.len() > NONCE_LEN,
            "Encrypted value too short (missing nonce)"
        );

        let (nonce_bytes, ciphertext) = blob.split_at(NONCE_LEN);
        let nonce = Nonce::from_slice(nonce_bytes);

        self.get_key(|key| {
            let key = Key::from_slice(key);
            let cipher = ChaCha20Poly1305::new(key);

            let plaintext_bytes = cipher.decrypt(nonce, ciphertext).map_err(|e| {
                let backend = self.key_source.backend_name();
                ::zeroclaw_log::record!(
                    ERROR,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                        .with_attrs(::serde_json::json!({

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Re-store the secret through the secrets CLI so it is freshly encrypted in the current format
  2. Verify the stored value is valid even-length hex and decodes to more than 12 bytes
  3. Confirm you are reading the same config/secrets file and key that produced the value

Example fix

# before: value truncated during copy (only 8 bytes after decode)
# after: re-store the secret to regenerate a full encrypted blob
zeroclaw config set-secret api_key   # then paste the plaintext when prompted
Defensive patterns

Strategy: validation

Validate before calling

fn looks_like_chacha20_ciphertext(v: &str) -> bool {
    v.len() % 2 == 0
        && v.chars().all(|c| c.is_ascii_hexdigit())
        && v.len() / 2 > 12 // > NONCE_LEN (12): nonce + at least 1 byte of ciphertext/tag
}

if !looks_like_chacha20_ciphertext(&stored_value) {
    // re-store the secret instead of attempting decrypt
}

Try / catch

catch the decrypt Result; on message "Encrypted value too short (missing nonce)" re-store the secret via the secrets CLI rather than retrying decrypt

Prevention

When it happens

Trigger: A truncated encrypted value (partial copy-paste into the config); manual edits of the secrets file; a plaintext or legacy-format value being fed into the ChaCha20 decrypt path; pointing decryption at the wrong field or file.

Common situations: Operators copying encrypted blobs by hand and losing characters; config files mangled by templating or line wrapping; mixing key files so an old-format value is read as ChaCha20; secrets values damaged in transit between environments.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/9a1ab3b412b2f71c. Report an issue: GitHub.