gitbutlerapp/gitbutler · error · napi::Error

argument ' ' must be a non-negative integer that fits in…

Error message

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

What it means

The but_api macro converts napi integer parameters (i64/f64 on the JS side) into Rust usize via TryFrom. When the supplied value is negative or exceeds the platform usize range, the conversion fails and this InvalidArg error names the argument.

Solutions

  1. Clamp or validate the value to be a non-negative integer before the call
  2. Replace sentinel negative values with the API's explicit option (e.g. omit the parameter)
  3. Check the arithmetic upstream that produced the negative value

Example fix

// before
api.listCommits(-10)
// after
api.listCommits(Math.max(0, offset))
Defensive patterns

Strategy: validation

Validate before calling

const isNonNegativeInt = (n) => Number.isInteger(n) && n >= 0;

Type guard

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

Try / catch

try { api.list(x); } catch (e) { if (/fits in usize/.test(e.message)) console.error('argument must be >= 0'); throw e; }

Prevention

When it happens

Trigger: Calling a generated but_api NAPI function whose parameter is remapped to usize and passing a negative number, a value above usize::MAX (rare on 64-bit), or a non-integral JS number.

Common situations: Frontend computes a limit/offset/index that went negative (e.g. cursor - pageSize), or passes -1 as a sentinel 'unlimited' value which the API does not accept.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/775918f01c2a50ee. Report an issue: GitHub.

Appendix: source

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

                    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 58e5313667)