{"record":{"id":"5000199d513ebcfe","repo":"risingwavelabs/risingwave","slug":"invalid-license-key","errorCode":null,"errorMessage":"invalid license key","messagePattern":"invalid license key","errorType":"error_code","errorClass":"LicenseError","httpStatus":null,"severity":"error","filePath":"src/license/src/manager.rs","lineNumber":202,"sourceCode":"impl Default for License {\n    /// The default license is a free license that never expires.\n    ///\n    /// Used when `license_key` is unset or invalid.\n    fn default() -> Self {\n        Self {\n            sub: \"default\".to_owned(),\n            tier: Tier::Free,\n            iss: Issuer::Prod,\n            rwu_limit: None,\n            exp: u64::MAX,\n        }\n    }\n}\n\n/// The error type for invalid license key when verifying as JWT.\n#[derive(Debug, Clone, Error)]\npub enum LicenseError {\n    #[error(\"invalid license key\")]\n    InvalidKey(#[source] jsonwebtoken::errors::Error),\n\n    #[error(\n        \"a valid license key is set, but it is currently not effective because the CPU core in the cluster \\\n        ({actual}) exceeds the maximum allowed by the license key ({limit}); \\\n        consider removing some nodes or acquiring a new license key with a higher limit\"\n    )]\n    CpuLimitExceeded { limit: u64, actual: u64 },\n\n    #[error(\n        \"a valid license key is set, but it is currently not effective because the memory in the cluster \\\n        ({actual}) exceeds the maximum allowed by the license key ({limit}); \\\n        consider removing some nodes or acquiring a new license key with a higher limit\",\n        actual = humansize::format_size(*actual, humansize::BINARY),\n        limit = humansize::format_size(*limit, humansize::BINARY),\n    )]\n    MemoryLimitExceeded { limit: u64, actual: u64 },\n}","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/license/src/manager.rs#L184-L220","documentation":"LicenseError::InvalidKey is returned when the license key set for the RisingWave cluster cannot be verified as a valid JWT. LicenseManager::refresh decodes the key with the embedded RSA public key (RS512) and issuer validation; any jsonwebtoken error (bad signature, expired, wrong issuer, malformed token) becomes InvalidKey with the underlying jsonwebtoken::errors::Error as its source. license() also returns this variant when the key has passed its `exp` claim.","triggerScenarios":"Calling LicenseManager::refresh(LicenseKey) with a key that is not a valid RS512 JWT signed by RisingWave's key (issuer prod.risingwave.com, or test.risingwave.com in debug builds), a key whose `exp` claim has passed when license() is later called, a corrupted/truncated key string, or a non-empty but garbage value in the license key config (system parameter `license_key`).","commonSituations":"Typo or truncation when copy-pasting the license key into config; an expired license that was valid at set-time; a test-issuer key used in a release (non-debug) build; whitespace/newlines introduced by shell quoting or YAML; upgrading from a key format the current binary no longer accepts.","solutions":["Re-copy the license key exactly as issued, without surrounding whitespace or quotes, and set it again via the `license_key` system parameter or config.","Check the `source` chain in the error log (jsonwebtoken::errors::Error) — if it is ExpiredSignature, obtain a renewed license key from RisingWave.","If using a `test.risingwave.com`-issued key, run a debug build of RisingWave; release builds only accept the `prod` issuer.","If no valid key is available, unset the key (empty value) to fall back to the default free license, then fix the key later."],"exampleFix":"// before (invalid/truncated key)\nSET GLOBAL license_key = 'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIi...truncated';\n// after (full, exact key)\nSET GLOBAL license_key = 'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.<full-payload>.<full-signature>';","handlingStrategy":"validation","validationCode":"// Rust: validate the key before setting it\nfn license_key_is_valid(key: &str) -> bool {\n    if key.is_empty() { return true; } // empty means default license\n    use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};\n    let key = DecodingKey::from_rsa_pem(include_bytes!(\"key.pub\")).unwrap();\n    let mut v = Validation::new(Algorithm::RS512);\n    v.set_issuer(&[\"prod.risingwave.com\"]);\n    decode(key_bytes, &key, &v).is_ok()\n}","typeGuard":"fn is_license_error(e: &LicenseError) -> Option<&jsonwebtoken::errors::Error> {\n    match e { LicenseError::InvalidKey(src) => Some(src), _ => None }\n}","tryCatchPattern":"// Rust has no try/catch; match on the Result\nmatch LicenseManager::get().license() {\n    Ok(license) => use_license(license),\n    Err(e @ LicenseError::InvalidKey(_)) => {\n        tracing::warn!(error = %e.as_report(), \"license invalid; falling back to free tier\");\n        fallback_to_default_license();\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Paste license keys programmatically (config management) instead of manual copy-paste to avoid truncation/whitespace.","Track license expiry and renew before the `exp` claim passes.","Use prod-issued keys in release builds; test-issuer keys only in debug builds.","Call Feature::check_available instead of caching the License result, since licenses can expire."],"tags":["license","jwt","rust","configuration"],"backgroundTag":"jwt-token-expired","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}