BoundaryML/baml · error · JwtError
JavaScript error: {0:?}
Error message
JavaScript error: {0:?} What it means
JwtError::JsError in the wasm JWT module wraps a raw wasm_bindgen JsValue produced by any failure in a browser JavaScript API call (e.g. SubtleCrypto.sign/importKey, fetch). The thiserror attribute formats it as 'JavaScript error: {0:?}'. It surfaces when BAML runs in WASM (browser) and a JS-side crypto operation rejects.
Source
Thrown at engine/baml-runtime/src/internal/wasm_jwt.rs:25
/// problematic for some toolchains to cross-compile to WASM.
///
/// At the time of writing, the Vertex provider is the only code in the
/// runtime that produces JWT's.
use base64::{
engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD},
Engine,
};
use js_sys::{Array, Object, Uint8Array};
use serde::{Deserialize, Serialize};
use serde_json::json;
use thiserror::Error;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::JsFuture;
use web_sys::{window, CryptoKey, SubtleCrypto};
#[derive(Error, Debug)]
pub enum JwtError {
#[error("JavaScript error: {0:?}")]
JsError(JsValue),
#[error("Base64 decode error: {0}")]
Base64Error(#[from] base64::DecodeError),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Missing window object")]
NoWindow,
#[error("Missing crypto API")]
NoCrypto,
}
impl From<JsValue> for JwtError {
fn from(err: JsValue) -> Self {
JwtError::JsError(err)
}
}
pub async fn encode_jwt(View on GitHub (pinned to bd85ce9dee)
Solutions
- Log the wrapped JsValue (serde-wasm-bindgen or console) to see the real JS message.
- Serve the app over HTTPS so the WebCrypto SubtleCrypto API is available.
- Verify the algorithm/parameters passed to importKey/sign are supported by the browser.
- Test in a modern browser and confirm window.crypto.subtle exists before calling.
Example fix
// before
let sig = JsFuture::from(subtle.sign(&algorithm, &key, &data).unwrap()).await?;
// after
match subtle.sign(&algorithm, &key, &data) {
Ok(p) => JsFuture::from(p).await.map_err(JwtError::JsError),
Err(e) => { web_sys::console::error_1(&e); Err(JwtError::JsError(e)) }
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!window.isSecureContext || !window.crypto?.subtle) {
throw new Error("WebCrypto SubtleCrypto unavailable; JWT signing will fail");
} Type guard
function hasSubtleCrypto(w) {
return typeof w.crypto?.subtle?.sign === "function";
} Try / catch
try {
const token = await signJwt(claims);
} catch (e) {
if (String(e).includes("JavaScript error")) {
console.error("WebCrypto failure:", e); // inspect wrapped JsValue
} else { throw e; }
} Prevention
- Serve over HTTPS so SubtleCrypto is available.
- Log wrapped JsValues during development to see underlying JS errors.
- Test the WASM JWT path in real target browsers early.
When it happens
Trigger: Calling the WASM JWT signer where window.crypto.subtle.sign/importKey returns a rejected promise; any web_sys JsFuture that resolves to an Err(JsValue).
Common situations: Running in a non-secure context (http://) where SubtleCrypto is unavailable or restricted; browser rejecting the key algorithm parameters; CORS or network failure in underlying fetch; very old browsers without WebCrypto.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- Missing crypto API
- Base64 decode error: {0}
- JSON error: {0}
- Missing window object
- Type error in JS callback: {0}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/796cd0fc74a73c52.
Report an issue: GitHub.