{"record":{"id":"1bd65d5280213cb7","repo":"juspay/hyperswitch","slug":"failed-to-gcp-kms-decrypt-input-data","errorCode":null,"errorMessage":"Failed to GCP KMS decrypt input data","messagePattern":"Failed to GCP KMS decrypt input data","errorType":"exception","errorClass":"GcpKmsError","httpStatus":null,"severity":"critical","filePath":"crates/external_services/src/gcp_kms/core.rs","lineNumber":156,"sourceCode":"\n        let output = consts::BASE64_ENGINE.encode(response.ciphertext);\n\n        let time_taken = start.elapsed();\n        metrics::GCP_KMS_ENCRYPT_TIME.record(time_taken.as_secs_f64(), &[]);\n\n        Ok(output)\n    }\n}\n\n/// Errors that could occur during GCP KMS operations.\n#[derive(Debug, thiserror::Error)]\npub enum GcpKmsError {\n    /// An error occurred when base64 decoding the input data.\n    #[error(\"Failed to base64 decode input data\")]\n    Base64DecodingFailed,\n\n    /// An error occurred when GCP KMS decrypting the input data.\n    #[error(\"Failed to GCP KMS decrypt input data\")]\n    DecryptionFailed,\n\n    /// An error occurred when GCP KMS encrypting the input data.\n    #[error(\"Failed to GCP KMS encrypt input data\")]\n    EncryptionFailed,\n\n    /// An error occurred UTF-8 decoding the GCP KMS decrypted output.\n    #[error(\"Failed UTF-8 decode of GCP KMS decrypted output\")]\n    Utf8DecodingFailed,\n\n    /// An error occurred when creating the GCP KMS client.\n    #[error(\"Failed to create GCP KMS client\")]\n    ClientCreationFailed,\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/juspay/hyperswitch/blob/806ec7dcc036d895b93313d5e8eaa2374e25bcdf/crates/external_services/src/gcp_kms/core.rs#L138-L174","documentation":"Thrown when the underlying google_cloud_kms client's decrypt RPC fails inside GcpKmsClient::decrypt. The request has already been base64 decoded successfully; this error means GCP Cloud KMS rejected or failed the DecryptRequest (network error, permission denied, key not found/enabled, or ciphertext was encrypted with a different key). The original gRPC error is logged (gcp_kms_error) and a failure metric incremented before this context is applied.","triggerScenarios":"Calling GcpKmsClient::decrypt and the inner_client.decrypt(request, None) RPC returning an error at crates/external_services/src/gcp_kms/core.rs:98-106. Causes include: wrong key name (project/location/keyRing/cryptoKey mismatch in GcpKmsConfig), IAM principal lacking roles/cloudkms.cryptoKeyDecrypter, key disabled or scheduled for destruction, ciphertext encrypted under a different key, malformed/truncated decoded ciphertext, or connectivity to cloudkms.googleapis.com failing.","commonSituations":"Config typo in project_id/location_id/key_ring_id/key_id; running locally or in CI without Application Default Credentials or with a service account missing KMS IAM roles; rotating or disabling a KMS key then decrypting old data; ciphertext copied from another environment/key; egress firewall blocking the KMS endpoint; gRPC channel issues in long-lived processes.","solutions":["Check the router_env log line 'Failed to GCP KMS decrypt data' with gcp_kms_error=?error — the underlying gRPC status (PermissionDenied, NotFound, InvalidArgument, Unavailable) pinpoints the cause","Verify the GcpKmsConfig key path matches the real resource: gcloud kms keys list --location <loc> --keyring <ring>","Confirm the authenticated principal has roles/cloudkms.cryptoKeyDecrypter on the key: gcloud kms keys get-iam-policy <key> --location <loc> --keyring <ring>","Ensure the ciphertext was encrypted with the same cryptoKey (or re-encrypt data after key rotation)","If Unavailable/DeadlineExceeded, check network/egress to cloudkms.googleapis.com and consider retrying with backoff"],"exampleFix":"// before: opaque failure\nlet secret = client.decrypt(b64_ciphertext).await?;\n\n// after: inspect and branch on the underlying error context via error_stack\nmatch client.decrypt(b64_ciphertext).await {\n    Ok(secret) => secret,\n    Err(e) if e.current_context().to_string().contains(\"GCP KMS decrypt\") => {\n        // read the logged gcp_kms_error / report ops: likely IAM, key state, or wrong key\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"retry","validationCode":"// Validate config before constructing the client\nif let Err(msg) = gcp_kms_config.validate() {\n    return Err(msg);\n}\n// Ensure ciphertext matches what encrypt() emits (standard base64)\nassert!(base64::engine::general_purpose::STANDARD\n    .decode(ciphertext.trim())\n    .is_ok());","typeGuard":null,"tryCatchPattern":"use error_stack::ContextExt;\n\nmatch client.decrypt(ct).await {\n    Ok(p) => p,\n    Err(e) if matches!(e.current_context(), GcpKmsError::DecryptionFailed) => {\n        // transient (Unavailable/DeadlineExceeded) vs permanent (PermissionDenied/NotFound):\n        // check the logged gcp_kms_error; retry only transient statuses with backoff\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Run GcpKmsConfig::validate() at startup and cross-check the key path with gcloud","Grant roles/cloudkms.cryptoKeyDecrypter and test with a known ciphertext in staging","Monitor the GCP_KMS_DECRYPTION_FAILURES metric and alert on spikes","Keep ciphertext and key version paired; store the key resource name alongside encrypted data"],"tags":["gcp-kms","grpc","rust","iam","decryption","cloud"],"backgroundTag":"cloud-kms-decrypt-failed","analyzedSha":"806ec7dcc036d895b93313d5e8eaa2374e25bcdf","analyzedAt":"2026-08-28T16:43:48.683Z","contentChangedAt":"2026-08-28T16:43:48.683Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}