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

  1. Inspect the args slice passed to invoke_cli and remove any embedded NUL bytes
  2. Sanitize/validate arguments before calling invoke_cli (reject strings containing '\0')
  3. 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

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


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