BoundaryML/baml · error
Attempting to finish a call {:#?} without first starting one
Error message
Attempting to finish a call {:#?} without first starting one. Current context {:#?} What it means
In `finish_call` (tracing/mod.rs:499), `ctx.exit()` returned None, meaning no call was ever started in the current runtime context. BAML requires a matching start_call/finish_call pair per context; finishing without starting is an internal invariant violation, and the message dumps the call and context for debugging.
Source
Thrown at engine/baml-runtime/src/tracing/mod.rs:499
function_type,
is_stream,
);
BAML_TRACER.lock().unwrap().put(Arc::new(trace_event));
call
}
#[cfg(target_arch = "wasm32")]
pub(crate) async fn finish_call(
&self,
call: TracingCall,
ctx: &RuntimeContextManager,
response: Option<BamlValue>,
) -> Result<uuid::Uuid> {
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. Current context {:#?}",
call,
ctx
);
};
if call.call_id != call_id {
anyhow::bail!("Call ID mismatch: {} != {}", call.call_id, call_id);
}
if let Some(tracer) = &self.tracer {
tracer
.submit(response.to_log_schema(&self.options, event_chain, tags, call))
.await?;
guard.done();
Ok(call_id)
} else {
guard.done();View on GitHub (pinned to bd85ce9dee)
Solutions
- Ensure every `finish_call` is paired with a successful `start_call` on the same RuntimeContextManager.
- Check for early-return/error paths that skip start_call but still reach finish_call; guard them.
- Verify the context manager instance is shared (not re-created) between the start and finish of the call.
- Ensure finish_call is not invoked twice for the same call — the first exit() consumes the stack entry.
Example fix
// before manager.finish_call(call, &ctx, Some(response)); // after: start first, finish exactly once manager.start_call(&ctx, ...); manager.finish_call(call, &ctx, Some(response));
Defensive patterns
Strategy: validation
Validate before calling
// ensure a call is active before finishing
if !ctx_has_active_call(&ctx) {
tracing::warn!("no active BAML call to finish; skipping");
return;
} Try / catch
if let Err(e) = runtime.finish_call(call, &ctx, response) {
if e.to_string().contains("without first starting one") {
log::debug!("unpaired finish_call ignored: {e}");
} else {
return Err(e);
}
} Prevention
- Always pair start_call/finish_call in the same scope (use a guard struct with Drop).
- Never create a new context manager between start and finish.
- Finish each call exactly once; track with a flag.
- Cover error paths in integration tests so start always precedes finish.
When it happens
Trigger: Calling `finish_call` on a `RuntimeContextManager` whose stack has no active (call_id, event_chain, tags) entry — e.g. the start call never ran, ran in a different context, or the context was already exited.
Common situations: Custom integrations (language bindings, async runtimes) that call finish without start after an early error path; context clones/moves across threads so the started call is not visible; double-finishing the same call.
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
- Call ID mismatch: {} != {}
- Attempting to finish a call without first starting one
- no-color diagnostics do not invoke the message highlighter
- rendering without a message highlighter cannot fail semantic
- diagnostic message style code is a valid Unicode variation s
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/b80ba4217a191c10.
Report an issue: GitHub.