{"record":{"id":"25d9ed86d597e49e","repo":"BoundaryML/baml","slug":"json-error-0","errorCode":null,"errorMessage":"JSON error: {0}","messagePattern":"JSON error: (.+?)","errorType":"exception","errorClass":"JwtError","httpStatus":null,"severity":"error","filePath":"engine/baml-runtime/src/internal/wasm_jwt.rs","lineNumber":29,"sourceCode":"use base64::{\n    engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD},\n    Engine,\n};\nuse js_sys::{Array, Object, Uint8Array};\nuse serde::{Deserialize, Serialize};\nuse serde_json::json;\nuse thiserror::Error;\nuse wasm_bindgen::JsValue;\nuse wasm_bindgen_futures::JsFuture;\nuse web_sys::{window, CryptoKey, SubtleCrypto};\n\n#[derive(Error, Debug)]\npub enum JwtError {\n    #[error(\"JavaScript error: {0:?}\")]\n    JsError(JsValue),\n    #[error(\"Base64 decode error: {0}\")]\n    Base64Error(#[from] base64::DecodeError),\n    #[error(\"JSON error: {0}\")]\n    JsonError(#[from] serde_json::Error),\n    #[error(\"Missing window object\")]\n    NoWindow,\n    #[error(\"Missing crypto API\")]\n    NoCrypto,\n}\n\nimpl From<JsValue> for JwtError {\n    fn from(err: JsValue) -> Self {\n        JwtError::JsError(err)\n    }\n}\n\npub async fn encode_jwt(\n    claims: &serde_json::Value,\n    private_key_pem: &str,\n) -> Result<String, JwtError> {\n    // Extract the crypto.subtle API","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-runtime/src/internal/wasm_jwt.rs#L11-L47","documentation":"JwtError::JsonError converts a serde_json::Error (via #[from]) when deserializing decoded JWT segments (header/claims) into structs fails. After base64 decoding, the bytes must be valid JSON matching the expected shape.","triggerScenarios":"Decoding a JWT whose payload segment is not valid JSON, or whose claims lack fields required by the target serde struct (missing/renamed keys, wrong types).","commonSituations":"Token from a nonstandard issuer with unexpected claim names; using a struct requiring fields the token omits; decoding garbage bytes; expiry/claims type mismatches (string vs number).","solutions":["Deserialize into serde_json::Value first and inspect the actual claims structure.","Make serde struct fields Option or add #[serde(default)] for claims that may be absent.","Verify the token payload decodes to JSON (e.g. echo in a JWT debugger).","Align struct field names with the token via #[serde(rename)] attributes."],"exampleFix":"// before\nstruct Claims { sub: String, admin: bool }\n// after\n#[derive(Deserialize)]\nstruct Claims { sub: String, #[serde(default)] admin: Option<bool> }","handlingStrategy":"type-guard","validationCode":"const claims = JSON.parse(atobUrlSafe(payloadSegment));\nif (typeof claims.sub !== \"string\") throw new Error(\"Missing sub claim\");","typeGuard":"function hasRequiredClaims(c) {\n  return typeof c?.sub === \"string\" && typeof c?.exp === \"number\";\n}","tryCatchPattern":"match serde_json::from_slice::<Claims>(&payload) {\n    Ok(claims) => Ok(claims),\n    Err(e) => {\n      let raw: serde_json::Value = serde_json::from_slice(&payload)?;\n      eprintln!(\"Unexpected claims: {}\", raw);\n      Err(e.into())\n    }\n}","preventionTips":["Make optional claims Option<T> or #[serde(default)] in your claims struct.","Inspect a sample token in a JWT debugger to align field names/types.","Handle issuers that emit nonstandard claims defensively."],"tags":["wasm","json","serde","jwt"],"backgroundTag":"json-unmarshal-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"}