BoundaryML/baml · error · EngineError

Future with ID {future_id} not found

Error message

Future with ID {future_id} not found

What it means

`EngineError::FutureNotFound` is raised when a `FutureId` cannot be resolved in the engine's future table. Futures are registered when spawned and removed once resolved or dropped; an unknown ID means it never existed here, was already completed, or was discarded.

Source

Thrown at baml_language/crates/bex_engine/src/lib.rs:804

        map.remove(&self.call_id);
        let no_active_calls = map.values().all(|call| call.pending);
        drop(map);
        if no_active_calls {
            self.engine.lifecycle_changed.notify_waiters();
        }
    }
}

/// Errors that can occur during engine execution.
#[derive(Debug, PartialEq, Error, Clone)]
pub enum EngineError {
    #[error("BAML engine is shutting down")]
    ShuttingDown,

    #[error("Function call with ID {call_id} not found")]
    FunctionCallNotFound { call_id: CallId },

    #[error("Future with ID {future_id} not found")]
    FutureNotFound { future_id: FutureId },

    #[error("Function not found: {name}")]
    FunctionNotFound { name: String },

    /// Function exists, but its `FunctionKind` is not invokable as an
    /// engine entry point. Only bytecode functions can be called via
    /// [`BexEngine::call_function`] / [`BexEngine::call_function_bound_args`]:
    /// native (`$rust_function`) entries would re-enter the VM through
    /// `YieldToCall` with no bytecode frame to return to. Sysops + builtins
    /// reach their natives from inside a calling bytecode body.
    #[error("Function `{name}` is not invokable as an entry point (kind: {kind})")]
    NotInvokableAsEntry { name: String, kind: String },

    #[error("VM internal error: {0}")]
    VmInternalError(bex_vm::errors::VmInternalError),

    #[error("{}", format_vm_internal_error(source, trace))]

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Await each future exactly once and drop the ID afterwards.
  2. Obtain fresh `FutureId`s from the current engine instance.
  3. Verify the future was spawned on the same engine being queried.

Example fix

// before
let a = engine.future_result(fid).await?;
let b = engine.future_result(fid).await?; // second lookup fails
// after
let a = engine.future_result(fid).await?;
// do not reuse fid after completion
Defensive patterns

Strategy: try-catch

Validate before calling

// keep a local registry of pending FutureIds issued by this engine

Try / catch

// rust
match engine.future_result(fid).await {
    Err(EngineError::FutureNotFound { .. }) => /* future already resolved or foreign */,
    other => other?,
}

Prevention

When it happens

Trigger: Polling, completing, or fetching the result of a future by an ID that the engine has already reaped or never issued.

Common situations: Double-awaiting a future's result; holding future IDs across engine shutdown/restart; ID mixups when multiple engines run concurrently.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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