BoundaryML/baml · error
substring() end argument must be an int at
Error message
substring() end argument must be an int at {:?} What it means
The second argument to substring (the end index) must be an integer. The interpreter destructures args[1] as BamlValueWithMeta::Int and bails with this message otherwise. The value is later clamped with .min(s.len()).max(start), but its type must still be Int.
Solutions
- Pass an integer as the end index; convert string/float values to int before the call.
- Check the type of the expression in argument position 1 at the failing call site.
- Note that out-of-bounds ends are already clamped, so only the type needs fixing — pass a large int to mean 'to the end'.
Example fix
// before let head = s.substring(0, endStr); // endStr: string // after let head = s.substring(0, endStr.to_int());
Defensive patterns
Strategy: validation
Validate before calling
// end index must be an int let end_ok = end is int;
Type guard
fn is_int(v: BamlValue) -> bool { matches!(v, BamlValue::Int(_)) } Try / catch
match result {
Err(e) if e.to_string().contains("end argument must be an int") => use_string_len_as_end,
other => other,
} Prevention
- Round/convert computed end positions to int before the call.
- Remember end is clamped to the string length, so a large int is a safe 'to the end' value.
- Type-annotate end index variables as int.
When it happens
Trigger: Calling s.substring(0, "5"), s.substring(0, endFloat), or passing a null/non-int expression as the end index in a BAML expression.
Common situations: End indices coming from string-typed parsed values; float results from division/arithmetic used as the end; optional end variables that are null at runtime.
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
- substring() start argument must be an int at
- array assignment requires a non-negative integer index at
- endsWith() argument must be a string at
- includes() argument must be a string at
- split() argument must be a string at
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/5fa1b7dd8e06d0df.
Report an issue: GitHub.
Appendix: source
Thrown at engine/baml-compiler/src/thir/interpret.rs:2961
}
"substring" => {
let BamlValueWithMeta::String(s, _) = receiver else {
bail!(
"substring() method only available on strings at {:?}",
meta.0
);
};
if args.len() != 2 {
bail!(
"substring() method takes exactly 2 arguments at {:?}",
meta.0
);
}
let BamlValueWithMeta::Int(start, _) = &args[0] else {
bail!("substring() start argument must be an int at {:?}", meta.0);
};
let BamlValueWithMeta::Int(end, _) = &args[1] else {
bail!("substring() end argument must be an int at {:?}", meta.0);
};
let start = (*start as usize).min(s.len());
let end = (*end as usize).min(s.len()).max(start);
Ok(BamlValueWithMeta::String(
s[start..end].to_string(),
meta.clone(),
))
}
"replace" => {
let BamlValueWithMeta::String(s, _) = receiver else {
bail!("replace() method only available on strings at {:?}", meta.0);
};
if args.len() != 2 {
bail!("replace() method takes exactly 2 arguments at {:?}", meta.0);
}
let BamlValueWithMeta::String(search, _) = &args[0] else {View on GitHub (pinned to bd85ce9dee)