BoundaryML/baml · error

Attempting to finish a call without first starting one

Error message

Attempting to finish a call without first starting one

What it means

In `finish_baml_call` (tracing/mod.rs:637), `ctx.exit()` returned None: no call was started in this context before attempting to finish the BAML function call. The runtime cannot close a trace that was never opened.

Source

Thrown at engine/baml-runtime/src/tracing/mod.rs:637

                call.function_type,
            )
        };

        BAML_TRACER.lock().unwrap().put(Arc::new(event));

        Ok(call_id)
    }

    #[cfg(target_arch = "wasm32")]
    pub(crate) async fn finish_baml_call(
        &self,
        call: TracingCall,
        ctx: &RuntimeContextManager,
        response: &Result<FunctionResult>,
    ) -> Result<(uuid::Uuid, Vec<baml_ids::FunctionCallId>)> {
        let guard = self.trace_stats.guard();
        let Some((call_id, event_chain, tags)) = ctx.exit() else {
            anyhow::bail!("Attempting to finish a call without first starting one");
        };

        if call.call_id != call_id {
            anyhow::bail!("Call ID mismatch: {} != {}", call.call_id, call_id);
        }

        if let Ok(response) = &response {
            let name = event_chain.last().map(|s| s.name.as_str());
            let is_ok = response
                .result_with_constraints()
                .as_ref()
                .is_some_and(|r| r.is_ok());
            if is_ok {
                baml_log::info!(
                    "{}{}",
                    name.map(|s| format!("Function {s}:\n"))
                        .unwrap_or_default()
                        .purple(),

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Ensure start_baml_call runs before finish_baml_call on the same context manager.
  2. Guard error paths so finish is only invoked when a start succeeded (track a started flag).
  3. Pass the same RuntimeContextManager instance used at start, not a new one.
  4. Call finish exactly once per started call.

Example fix

// before
let result = run_function().await; // may fail before tracing started
runtime.finish_baml_call(call, &ctx, &result);
// after
runtime.start_baml_call(&mut call, &ctx, ...)?;
let result = run_function().await;
runtime.finish_baml_call(call, &ctx, &result);
Defensive patterns

Strategy: try-catch

Validate before calling

// track started state explicitly
let mut started = false;
runtime.start_baml_call(&mut call, &ctx, ...)?;
started = true;
if started { runtime.finish_baml_call(call, &ctx, &result)?; }

Try / catch

if let Err(e) = runtime.finish_baml_call(call, &ctx, &result) {
    if e.to_string().contains("without first starting one") {
        log::debug!("no active BAML call; nothing to finish");
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Calling `finish_baml_call` when `start_baml_call` (or equivalent) was not run on the same RuntimeContextManager, the context was already exited, or the wrong context instance was passed.

Common situations: Custom runtime integrations (Python/Ruby/FFI hosts) that finish the call on an error path before starting it; context created per-request but reused across requests after exit; double-finish.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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