{"record":{"id":"97f805d6a0f4f989","repo":"risingwavelabs/risingwave","slug":"stage-stage-reason-reason","errorCode":null,"errorMessage":"{stage:?} stage, reason: {reason}","messagePattern":"(.+?) stage, reason: (.+?)","errorType":"error_code","errorClass":"CryptographyError","httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/scalar/encrypt.rs","lineNumber":192,"sourceCode":"    config.eval(data, CryptographyStage::Decrypt)\n}\n\n#[function(\n    \"encrypt(bytea, bytea, varchar) -> bytea\",\n    prebuild = \"CipherConfig::parse_cipher_config($1, $2)?\"\n)]\nfn encrypt(data: &[u8], config: &CipherConfig) -> Result<Box<[u8]>, CryptographyError> {\n    config.eval(data, CryptographyStage::Encrypt)\n}\n\n#[derive(Debug)]\nenum CryptographyStage {\n    Encrypt,\n    Decrypt,\n}\n\n#[derive(Debug, thiserror::Error)]\n#[error(\"{stage:?} stage, reason: {reason}\")]\nstruct CryptographyError {\n    pub stage: CryptographyStage,\n    #[source]\n    pub reason: openssl::error::ErrorStack,\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_decrypt() {\n        let data = b\"hello world\";\n        let mode = \"aes\";\n\n        let config = CipherConfig::parse_cipher_config(\n            b\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0A\\x0B\\x0C\\x0D\\x0E\\x0F\" as &[u8],\n            mode,","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/scalar/encrypt.rs#L174-L210","documentation":"CryptographyError is a thiserror error wrapper for openssl failures in encrypt/decrypt scalar functions. Its message reports which stage (Encrypt or Decrypt) failed plus the underlying openssl::error::ErrorStack as the source, so the real cause (key size, padding, bad input length) is in the chained source.","triggerScenarios":"Invoking the encrypt() or decrypt() SQL functions when the OpenSSL crypto operation returns an ErrorStack — e.g. wrong key length for AES, input not a multiple of the block size, or corrupted ciphertext during decrypt.","commonSituations":"Encrypting data with a key that doesn't match the required AES key size; decrypting data encrypted elsewhere with different parameters; OpenSSL misconfiguration or FIPS provider restrictions.","solutions":["Inspect the chained openssl::error::ErrorStack source (`err.source()`) for the concrete OpenSSL reason code.","Verify the encryption key length matches the algorithm (16/24/32 bytes for AES-128/192/256).","Ensure ciphertext length is a valid multiple of the cipher block size and padding settings match between encrypt and decrypt."],"exampleFix":"// before\nlet key = user_key.as_bytes(); // arbitrary length\n// after\nlet key = derive_key(user_key.as_bytes(), 32); // pad/hash to required AES-256 key length","handlingStrategy":"try-catch","validationCode":"if key.len() != 16 && key.len() != 24 && key.len() != 32 {\n    return Err(\"AES key must be 16, 24, or 32 bytes\".into());\n}","typeGuard":null,"tryCatchPattern":"match encrypt_fn(key, plaintext) {\n    Ok(ct) => ct,\n    Err(e) => {\n        // inspect openssl source chain\n        let mut src = e.source();\n        while let Some(s) = src { eprintln!(\"caused by: {s}\"); src = s.source(); }\n        return Err(anyhow!(\"crypto failed: {e}\"));\n    }\n}","preventionTips":["Validate key length against the cipher before encrypting/decrypting.","Use the same algorithm, padding, and IV parameters on both encrypt and decrypt sides.","Log the openssl ErrorStack reason codes when diagnosing."],"tags":["openssl","encryption","crypto"],"backgroundTag":"invalid-argument-value","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}