BoundaryML/baml · error
invalid arg
Error message
invalid arg
What it means
Panic raised inside `invoke_cli` in the baml Rust FFI layer when converting a CLI argument to a `CString`. `CString::new` fails if the argument contains an interior NUL byte (`\0`), which cannot be represented in a C string. The `expect` turns that conversion failure into an immediate panic with message "invalid arg".
Source
Thrown at languages/rust/baml/src/lib.rs:174
cffi_value_holder::Value::EnumValue(_) => "enum",
cffi_value_holder::Value::LiteralValue(_) => "literal",
cffi_value_holder::Value::ObjectValue(_) => "object",
cffi_value_holder::Value::ListValue(_) => "list",
cffi_value_holder::Value::MapValue(_) => "map",
cffi_value_holder::Value::UnionVariantValue(_) => "union",
cffi_value_holder::Value::CheckedValue(_) => "checked",
cffi_value_holder::Value::StreamingStateValue(_) => "streaming_state",
}
}
}
/// Call baml-cli with the given arguments
/// Returns the exit code
pub fn invoke_cli(args: &[&str]) -> i32 {
// Convert args to C strings
let c_args: Vec<CString> = args
.iter()
.map(|s| CString::new(*s).expect("invalid arg"))
.collect();
// Create array of pointers
let c_arg_ptrs: Vec<*const libc::c_char> = c_args
.iter()
.map(|s| s.as_ptr())
.chain(std::iter::once(std::ptr::null())) // null terminator
.collect();
#[allow(unsafe_code, clippy::print_stderr)]
unsafe {
match ffi::invoke_runtime_cli(c_arg_ptrs.as_ptr()) {
Ok(code) => code,
Err(e) => {
// CLI errors should be printed to stderr
eprintln!("Failed to load BAML library: {e}");
1
}View on GitHub (pinned to bd85ce9dee)
Solutions
- Inspect the args slice passed to invoke_cli and remove any embedded NUL bytes
- Sanitize/validate arguments before calling invoke_cli (reject strings containing '\0')
- Use CString::new(...).map_err(...) instead of expect if you need a graceful error
Example fix
// before
let c_args: Vec<CString> = args.iter().map(|s| CString::new(*s).expect("invalid arg")).collect();
// after
let c_args: Vec<CString> = args.iter().map(|s| CString::new(*s).expect("invalid arg")).collect();
let c_args: Vec<CString> = args.iter().map(|s| CString::new(*s).unwrap_or_else(|e| panic!("invalid arg {:?}: {}", s, e))).collect(); Defensive patterns
Strategy: validation
Validate before calling
fn valid_args(args: &[&str]) -> bool { args.iter().all(|a| !a.contains('\0')) }
if !valid_args(&args) { return -1; }
let code = invoke_cli(&args); Type guard
fn is_nul_free(s: &str) -> bool { !s.as_bytes().contains(&b'\0') } Try / catch
// panic-based; use catch_unwind if you must let code = std::panic::catch_unwind(|| invoke_cli(&args)).unwrap_or(-1);
Prevention
- Never embed NUL bytes in CLI arguments
- Validate string arguments before passing to FFI-style APIs
- Replace expect() with proper error handling in your own fork
When it happens
Trigger: Calling `invoke_cli(&[...])` with an argument string containing a NUL byte (e.g. `"baml\0--help"`). Normal argument lists (version, help, subcommands) never trigger it.
Common situations: Programmatically building argv arrays where arguments are concatenated or sourced from buffers that accidentally include NUL terminators; passing raw binary data instead of text.
Understand the failure class
Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.
Related errors
- Function panicked: {s}
- cannot determine directory of current executable
- {err}
- unsupported pack target `{target}`. {err}
- unsupported pack target `{target_triple}`
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/912b97f7c69ca615.
Report an issue: GitHub.