BoundaryML/baml · error

TimeZoneOffset.from_duration: expected Duration instance

Error message

TimeZoneOffset.from_duration: expected Duration instance

What it means

This panic occurs in TimeZoneOffset.from_duration when the argument is not a Duration object instance allocated by the VM. vm.as_instance().expect() panics for nulls, numbers, strings, or objects of any other class instead of returning an error.

Source

Thrown at baml_language/crates/bex_vm/src/package_baml/time.rs:449

        let mut out = String::new();
        format_clock_into(
            &mut out,
            u8::try_from(ns / NANOS_PER_HOUR).expect("hour fits in u8"),
            u8::try_from(ns / NANOS_PER_MINUTE % 60).expect("minute fits in u8"),
            u8::try_from(ns / NANOS_PER_SECOND % 60).expect("second fits in u8"),
            u32::try_from(ns % NANOS_PER_SECOND).expect("subsecond fits in u32"),
        );
        bex_str::BexStr::from(out)
    }
}

#[allow(clippy::used_underscore_items)] // Autogenerated from private BAML fields
impl BamlClassTimeTimeZoneOffset for PackageBamlImpl {
    fn from_duration(vm: &mut BexVm, duration: &Value) -> Result<Value, VmRustFnError> {
        let nanos = {
            let instance = vm
                .as_instance(duration)
                .expect("TimeZoneOffset.from_duration: expected Duration instance");
            view::time::Duration { instance }._nanoseconds()
        };
        let offset_ns = i64::try_from(&*nanos)
            .ok()
            .filter(|ns| ns.abs() <= MAX_TZ_OFFSET_NS)
            .ok_or_else(|| {
                invalid_argument(
                    "TimeZoneOffset out of range: must be within ±24 hours".to_string(),
                )
            })?;
        Ok(copy::time::TimeZoneOffset {
            _nanoseconds: offset_ns,
        }
        .to_value(vm))
    }
}

impl BamlClassTimeZonedDateTime for PackageBamlImpl {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Construct a Duration first (e.g. Duration.of_hours(5)) and pass that instance.
  2. Parse strings with Duration.parse before conversion.
  3. Type-check the argument before the call.
  4. Report a bug if a real Duration instance triggers the panic.

Example fix

// before
TimeZoneOffset.from_duration(5 * 3600 * 1_000_000_000) // int, panics
// after
TimeZoneOffset.from_duration(Duration.of_hours(5))
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_duration(v: &Value, vm: &BexVm) -> bool {
    vm.as_instance(v).map(|i| i.class() == "Duration").unwrap_or(false)
}

Type guard

fn as_duration<'a>(vm: &'a BexVm, v: &Value) -> Option<InstanceRef<'a>> {
    vm.as_instance(v).ok().filter(|i| i.class() == "Duration")
}

Try / catch

// BAML side
if v is Duration { TimeZoneOffset.from_duration(v) } else { Error("expected Duration") }

Prevention

When it happens

Trigger: Calling TimeZoneOffset.from_duration() with a non-Duration value: an int of nanoseconds, a string like 'PT5H', null, or a PlainTime/other instance.

Common situations: Passing a raw nanosecond count or ISO-8601 duration string instead of a Duration instance; deserialized durations losing instance identity through JSON.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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