BoundaryML/baml · error · napi::Error

{e:?}

Error message

{e:?}

What it means

Span::new fails when the args JSON passed to start a call cannot be deserialized into a BamlValue, or when env_vars cannot be deserialized into HashMap<String,String>; the raw serde error is formatted with {e:?}. It indicates malformed input to the span/tracing API.

Solutions

  1. Ensure args is a JSON object of serializable values before calling Span.new
  2. Ensure env_vars is a flat object of string-to-string (e.g. { ...process.env })
  3. Read the serde Debug error {e:?} to see the exact field/type that failed
  4. Wrap args/env_vars in JSON.stringify/parse to normalize them

Example fix

// before
const span = ctx.startSpan('MyFunc', args, ctxManager, process.env);
// after
const envVars = Object.fromEntries(Object.entries(process.env).map(([k, v]) => [k, String(v ?? '')]));
const span = ctx.startSpan('MyFunc', JSON.parse(JSON.stringify(args)), ctxManager, envVars);
Defensive patterns

Strategy: validation

Validate before calling

function assertSpanInputs(args, envVars) {
  if (typeof args !== 'object' || args === null || Array.isArray(args)) throw new Error('span args must be a JSON object');
  const badEnv = Object.entries(envVars ?? {}).filter(([, v]) => typeof v !== 'string');
  if (badEnv.length) throw new Error('env_vars values must all be strings: ' + badEnv.map(([k]) => k).join(','));
}

Type guard

const isValidSpanArgs = (args: unknown): args is Record<string, unknown> =>
  typeof args === 'object' && args !== null && !Array.isArray(args);

Try / catch

let span;
try {
  span = ctx.startSpan(fnName, args, ctxManager, envVars);
} catch (e) {
  console.error('startSpan failed:', String(e));
  span = null; // proceed without tracing rather than crashing
}

Prevention

When it happens

Trigger: Calling Span.new (via the runtime context manager) with args that aren't valid JSON values BAML understands, or env_vars containing non-string values (numbers, booleans, nested objects).

Common situations: Passing JS objects with unsupported field types, passing process.env values that were coerced incorrectly, or sending args that aren't a JSON object at all.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at engine/language_client_typescript/src/types/span.rs:27

crate::lang_wrapper!(BamlSpan,
  Option<baml_runtime::tracing::TracingCall>,
  no_from,
  rt: std::sync::Arc<CoreBamlRuntime>
);

#[napi]
impl BamlSpan {
    #[napi(ts_return_type = "BamlSpan")]
    pub fn new(
        runtime: &BamlRuntime,
        function_name: String,
        args: serde_json::Value,
        ctx: &RuntimeContextManager,
        env_vars: serde_json::Value,
    ) -> napi::Result<Self> {
        let args: BamlValue = serde_json::from_value(args)
            .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{e:?}")))?;
        let Some(args_map) = args.as_map() else {
            return Err(invalid_argument_error("Invalid span args"));
        };

        let env_vars: HashMap<String, String> = serde_json::from_value(env_vars)
            .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{e:?}")))?;

        let call = runtime
            .inner
            .start_call(&function_name, args_map, &ctx.inner, &env_vars);
        log::trace!("Starting call: {call:#?} for {function_name:?}\n");
        Ok(Self {
            inner: call.into(),
            rt: runtime.inner.clone(),
        })
    }

    // mthod to finish

View on GitHub (pinned to bd85ce9dee)