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

  1. Log the wrapped JsValue (serde-wasm-bindgen or console) to see the real JS message.
  2. Serve the app over HTTPS so the WebCrypto SubtleCrypto API is available.
  3. Verify the algorithm/parameters passed to importKey/sign are supported by the browser.
  4. 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

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


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/796cd0fc74a73c52. Report an issue: GitHub.