{"record":{"id":"ad490579d858402f","repo":"BoundaryML/baml","slug":"aborterror-detailed-message","errorCode":null,"errorMessage":"AbortError: {detailed_message}","messagePattern":"AbortError: (.+?)","errorType":"exception","errorClass":"ExposedError::AbortError","httpStatus":null,"severity":"warning","filePath":"engine/baml-runtime/src/internal/llm_client/orchestrator/call.rs","lineNumber":85,"sourceCode":"        Some(token) => Box::pin(async move {\n            token.cancelled_owned().await;\n        })\n            as std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>,\n        None => Box::pin(futures::future::pending()),\n    };\n    tokio::pin!(cancel_future);\n\n    for node in iter {\n        // Check for cancellation at the start of each iteration\n        let cancel_scope = node.scope.clone();\n        tokio::select! {\n            biased;\n\n            _ = &mut cancel_future => {\n                results.push((\n                    cancel_scope,\n                    LLMResponse::Cancelled(\"Operation cancelled\".to_string()),\n                    Some(Err(anyhow::anyhow!(\n                        crate::errors::ExposedError::AbortError {\n                            detailed_message: String::new()\n                        }\n                    ))),\n                ));\n                break;\n            }\n            result = async {\n                let prompt = match node.render_prompt(ir, prompt, ctx, params).await {\n                    Ok(p) => p,\n                    Err(e) => {\n                        return Some((\n                            node.scope,\n                            LLMResponse::InternalFailure(e.to_string()),\n                            Some(Err(anyhow::anyhow!(e.to_string()))),\n                        ));\n                    }\n                };","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/engine/baml-runtime/src/internal/llm_client/orchestrator/call.rs#L67-L103","documentation":"ExposedError::AbortError is raised by the orchestrator when a BAML call is cancelled while awaiting LLM responses. In the tokio::select! race, if the cancel_future completes first, the runtime records a Cancelled LLMResponse and an AbortError with a (here empty) detailed message, signaling the operation was aborted before completion.","triggerScenarios":"Any orchestrate() run where the caller's cancellation future resolves first: HTTP client disconnects, dropped request handles, explicit aborts, or timeout-driven cancellation while an LLM call is in flight.","commonSituations":"Browser users navigate away or React Query aborts fetches; server middleware times out requests; downstream consumers drop the stream/token driving the BAML function call.","solutions":["Handle the abort gracefully: catch AbortError and return a 499/cancelled-style response rather than a 500.","Check caller-side cancellation (AbortController, request context) is intentional; fix accidental future-dropping in your code.","If aborts are spurious, ensure the future/tokio task driving orchestrate() is not dropped early (hold the JoinHandle or use select branches deliberately).","Add retry-with-backoff only for idempotent calls if cancellation is timeout-based."],"exampleFix":"// before\nlet result = baml_fn.run(ctx, params).await?; // AbortError surfaces as 500\n// after\nmatch baml_fn.run(ctx, params).await {\n    Err(e) if is_abort(&e) => Ok(HttpResponse::cancelled()),\n    other => other.map_err(Into::into),\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_abort_error(err: &anyhow::Error) -> bool {\n    err.downcast_ref::<crate::errors::ExposedError::AbortError>().is_some()\n}","tryCatchPattern":"match orchestrate_result {\n    Err(e) if is_abort_error(&e) => {\n        tracing::info!(\"llm call aborted by client\");\n        return Ok(Cancelled);\n    }\n    other => other?,\n}","preventionTips":["Ensure the task/future driving the LLM call outlives the request; don't drop JoinHandles that carry the call.","Set explicit, generous timeouts so cancellations are intentional rather than incidental.","Map AbortError to a client-disconnect status (e.g. HTTP 499) in your API layer instead of surfacing it as a server error."],"tags":["llm","rust","cancellation","abort","async"],"backgroundTag":"request-timeout","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}