BoundaryML/baml · error · anyhow::Error
No root state found for local variable
Error message
No root state found for local variable: {:?} What it means
When building a watch Notification for a local-variable node, from_node_id looks up the variable's root state in vm.watch. The map lookup failing means the local variable (by stack index) has no registered root state — i.e. the variable was never put under watch or the state was cleared — yet a notification was requested for it.
Solutions
- Verify the variable was registered via watch before requesting notifications for its NodeId
- Check that the stack_index captured when subscribing still matches the current frame layout
- Guard the call site so notifications are only fetched for variables with an active root state
- Clear pending LocalVar notification requests when watches are unsubscribed or frames are popped
Example fix
// before
vm.watch.root_state(*node_id)
.map(|state| Notification::Channel(state.channel.clone()))
.ok_or_else(|| anyhow::anyhow!("No root state found for local variable: {:?}", stack_index))
// after
match vm.watch.root_state(*node_id) {
Some(state) => Ok(Notification::Channel(state.channel.clone())),
None => Ok(Notification::Ignored), // variable is no longer watched
} Defensive patterns
Strategy: fallback
Validate before calling
fn is_watched(vm: &Vm, node: &watch::NodeId) -> bool {
vm.watch.root_state_for(node).is_some()
} Try / catch
match Notification::from_node_id(vm, node_id) { Ok(n) => n, Err(_) => Notification::Ignored } Prevention
- Register the watch and only then enqueue notification requests
- Clean up pending LocalVar notifications when frames are popped or watches unsubscribed
- Log stack indices at subscription time and compare with lookup time to catch layout drift
When it happens
Trigger: Calling from_node_id with NodeId::LocalVar(stack_index) for a variable that has no root state registered in vm.watch (never subscribed, or subscription dropped/cleared before the notification was generated).
Common situations: Debugger/watch tooling requesting notifications for stack slots after a frame was popped or before a watch subscription completed; off-by-one stack indices after register allocation changes.
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
- Attempting to finish a call
- Attempting to finish a call without first starting one
- baml.panics.AssertionFailed
- baml.panics.Cancelled
- baml.panics.DivisionByZero
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/d7338326b0d7eed6.
Report an issue: GitHub.
Appendix: source
Thrown at engine/baml-vm/src/test.rs:219
pub fn viz(function_name: String, event: VizExecEvent) -> Self {
Notification::Viz {
function_name,
event,
}
}
}
impl Notification {
/// Convert from VM NodeId to test Node by resolving indices to names/objects.
pub fn from_node_id(node_id: &watch::NodeId, vm: &Vm) -> anyhow::Result<Self> {
match node_id {
watch::NodeId::LocalVar(stack_index) => vm
.watch
.root_state(*node_id)
.map(|state| Notification::Channel(state.channel.clone()))
.ok_or_else(|| {
anyhow::anyhow!("No root state found for local variable: {:?}", stack_index)
}),
watch::NodeId::HeapObject(_obj_index) => {
Ok(Notification::Object(Object::String("bogger".to_string())))
}
}
}
}
/// Enhanced test execution state that supports test Value comparisons.
#[derive(Debug, Clone, PartialEq)]
pub enum ExecState {
/// VM cannot proceed. It is awaiting a pending future to complete.
Await(Object),
/// VM notifies caller about a future that needs to be scheduled.
ScheduleFuture(Object),
/// VM has completed the execution with a test-friendly value.
Complete(Value),
View on GitHub (pinned to bd85ce9dee)