{"record":{"id":"57c13acb7bc5fcc0","repo":"BoundaryML/baml","slug":"e-wasm-auth","errorCode":null,"errorMessage":"{e:?}","messagePattern":"\\{e:\\?\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/baml-runtime/src/internal/llm_client/primitive/vertex/wasm_auth.rs","lineNumber":149,"sourceCode":"        }\n    }\n}\n\n#[derive(Debug, Deserialize)]\npub struct ServiceAccount {\n    pub token_uri: String,\n    pub project_id: String,\n    pub client_email: String,\n    pub private_key: String,\n}\n\nimpl ServiceAccount {\n    async fn get_oauth2_token(&self) -> Result<Token> {\n        let claims = Claims::from_service_account(self);\n\n        let jwt = encode_jwt(&serde_json::to_value(claims)?, &self.private_key)\n            .await\n            .map_err(|e| anyhow::anyhow!(format!(\"{e:?}\")))?;\n\n        // Make the token request\n        let client = reqwest::Client::new();\n        let params = [\n            (\"grant_type\", \"urn:ietf:params:oauth:grant-type:jwt-bearer\"),\n            (\"assertion\", &jwt),\n        ];\n        let res = client\n            .post(&self.token_uri)\n            .form(&params)\n            .send()\n            .await?\n            .text()\n            .await?;\n\n        parse_token_response(&res).context(format!(\"OAuth2 access token request failed: {res}\"))\n    }\n}","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-runtime/src/internal/llm_client/primitive/vertex/wasm_auth.rs#L131-L167","documentation":"When exchanging the signed JWT for an OAuth2 access token with Google's token endpoint, any failure from `encode_jwt` (JWT signing/encoding) is re-thrown with its Debug formatting. This surfaces JWT construction failures — most commonly key parsing or serialization errors — without a friendly message.","triggerScenarios":"Calling `ServiceAccount::get_oauth2_token` where `encode_jwt(&claims, &self.private_key)` fails: malformed/unsupported PEM private key, JSON serialization failure of the claims, or an encoding library error.","commonSituations":"A service-account JSON whose `private_key` contains unescaped newlines (copy-pasted or YAML-mangled), a key not in PKCS#8/RSA format, or an expired/rotated service account key.","solutions":["Inspect the inner error text (`e:?` in the message) to identify whether it is key parsing or serialization; read the full Debug string in logs.","Re-download the service account JSON key from GCP Console and load it verbatim (fix escaped `\\n` in private_key).","Ensure the private key is a supported format (RSA PEM, PKCS#8) with correct newlines; convert if needed with `openssl`.","Rotate to a fresh, non-deleted service account key."],"exampleFix":"// before: private_key pasted into YAML, newlines literal\nprivate_key: \"-----BEGIN PRIVATE KEY-----\\n...broken\"\n\n// after: load from the JSON key file so real newlines are preserved\nlet sa: ServiceAccount = serde_json::from_str(&std::fs::read_to_string(\"sa-key.json\")?)?;","handlingStrategy":"try-catch","validationCode":"// Validate the private key parses before token exchange\nconst key = sa.private_key;\nif (!key || !key.includes('-----BEGIN') || !key.includes('\\\\n') === false && key.split('\\n').length < 2) {\n  throw new Error('service account private_key malformed');\n}","typeGuard":"function hasValidPrivateKey(sa) { return typeof sa.private_key === 'string' && sa.private_key.startsWith('-----BEGIN') && sa.private_key.includes('END'); }","tryCatchPattern":"try { await getOAuth2Token(sa); } catch (e) { console.error('JWT/token exchange failed:', e); throw new Error('Check service-account private_key format and freshness'); }","preventionTips":["Always load the service-account JSON via a parser; never paste private_key into YAML/env with mangled newlines","Rotate and re-download keys instead of reusing old ones","Log the inner Debug error from {e:?} to distinguish key vs serialization failures"],"tags":["jwt","gcp","authentication","oauth2"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}