gitbutlerapp/gitbutler · error · napi::Error

InvalidArg

InvalidArg

Error message

argument '{}' must be a non-negative integer that fits in usize

What it means

Generated by but-api-macros for napi-exported functions taking usize: the JS value arrives as i64 and is narrowed with TryFrom; a negative number (or a value beyond the platform usize range, relevant on 32-bit builds) yields napi Status::InvalidArg with this message naming the argument. It is a boundary type-check that runs before the Rust function executes; fractional/NaN input is rejected earlier by napi's own number conversion.

Source

Thrown at crates/but-api-macros/src/lib.rs:1560

                    names.push(ident.to_string());
                    conversions.push(quote! {
                        let #ident: crate::json::HexHash = ::std::str::FromStr::from_str(&#ident)
                            .map(crate::json::HexHash)
                            .map_err(|e: gix::hash::decode::Error| napi::Error::new(napi::Status::InvalidArg, format!("{e}")))?;
                    });
                    call_arg_idents.push(quote! { #ident });
                }
                _ => {
                    // For all other types: check for napi-incompatible types first
                    if let Some(napi_ty) = napi_type_remap(base_ty) {
                        // Type needs remapping (e.g., usize → i64)
                        params.push(quote! { #ident: #napi_ty });
                        names.push(ident.to_string());
                        let arg_name = ident.to_string();
                        let conversion = match type_name.as_deref() {
                            Some("usize") => quote! {
                                let #ident: usize = ::std::convert::TryFrom::try_from(#ident).map_err(|_| {
                                    napi::Error::new(
                                        napi::Status::InvalidArg,
                                        format!(
                                            "argument '{}' must be a non-negative integer that fits in usize",
                                            #arg_name
                                        ),
                                    )
                                })?;
                            },
                            Some("isize") => quote! {
                                let #ident: isize = ::std::convert::TryFrom::try_from(#ident).map_err(|_| {
                                    napi::Error::new(
                                        napi::Status::InvalidArg,
                                        format!(
                                            "argument '{}' must be an integer that fits in isize",
                                            #arg_name
                                        ),
                                    )
                                })?;

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Validate in JS before the call: integer, >= 0, and <= Number.MAX_SAFE_INTEGER.
  2. Replace -1 sentinels with an explicit optional/null parameter in the binding signature.
  3. Clamp or reject out-of-range inputs with a user-facing message at the caller.

Example fix

// before
await listEntries({ limit: -1 }); // "argument 'limit' must be a non-negative integer..."

// after
const limit = userLimit < 0 ? undefined : userLimit; // omit to use the Rust default
await listEntries({ limit });
Defensive patterns

Strategy: validation

Validate before calling

if (!Number.isInteger(limit) || limit < 0 || !Number.isSafeInteger(limit)) {
  throw new TypeError('limit must be a non-negative safe integer');
}

Type guard

const isUsize = (v: unknown): v is number =>
  typeof v === 'number' && Number.isInteger(v) && v >= 0 && v <= Number.MAX_SAFE_INTEGER;

Prevention

When it happens

Trigger: From Node/Electron, calling a generated but-api binding whose Rust signature takes usize with -1 (a common 'unlimited' sentinel), values above Number.MAX_SAFE_INTEGER, or values above u32 range on 32-bit targets.

Common situations: UI code passing -1 as 'no limit'; arithmetic overflow producing huge counts in computed sizes; JSON configs deserialized with negative defaults.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/4f60e082a2ccf9a8. Report an issue: GitHub.