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
- Ensure start_baml_call runs before finish_baml_call on the same context manager.
- Guard error paths so finish is only invoked when a start succeeded (track a started flag).
- Pass the same RuntimeContextManager instance used at start, not a new one.
- 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
- Guard finish with a started flag set only after a successful start.
- Reuse the same RuntimeContextManager from start to finish.
- Call finish exactly once per invocation.
- Test integration error paths where start may be skipped.
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
- Attempting to finish a call {:#?} without first starting one
- Call ID mismatch: {} != {}
- Trace publisher channel is closed
- -32002
- LSP client sent exit before completing shutdown
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/e4452b09e970a86f.
Report an issue: GitHub.