gitbutlerapp/gitbutler · error · napi::Error

{e}

Error message

{e}

What it means

Generated in `build_napi_params` for parameters whose type maps through a serde_json `Value` transport: `serde_json::from_value` fails while converting the JS-supplied value into the transport type, producing a napi InvalidArg error carrying serde_json's message (`{e}`). Means the JSON shape sent from JS didn't match the expected transport struct/enum.

Solutions

  1. Fix the JS argument so it matches the expected serde shape (field names and types).
  2. Regenerate/inspect the TS definitions emitted by the macro to see the expected shape.
  3. Log the parameter value before the call to spot mistyped fields.
  4. Update the Rust transport struct to accept the shape callers actually send, if the new shape is intended.

Example fix

// before
api.createAssignment({ projectId: 123, commitId: "abc" }) // numeric id not accepted
// after
api.createAssignment({ projectId: "123", commitId: "abc" })
Defensive patterns

Strategy: validation

Validate before calling

function assertMatchesShape(obj, requiredFields) {
  if (obj == null || typeof obj !== 'object') throw new TypeError('expected object');
  for (const f of requiredFields) {
    if (obj[f] === undefined) throw new TypeError(`missing field: ${f}`);
  }
  return obj;
}
// call before the API:
assertMatchesShape(arg, ['projectId', 'commitId']);

Type guard

function isPlainObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }

Try / catch

try { await api.fn(arg); }
catch (e) { if (e?.code === 'InvalidArg') console.error('bad JSON shape:', arg); throw e; }

Prevention

When it happens

Trigger: Calling a `but_api` function from JS passing an object with missing/extra/mistyped fields for a parameter that is deserialized from serde_json::Value (e.g. wrong nested types, string instead of number).

Common situations: SDK/TS type drift after a Rust struct changed; hand-built plain objects missing required fields; sending undefined/null where a field is required; camelCase vs snake_case key mismatch.

Related errors


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

Appendix: source

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

        let ident = &pat_ident.ident;

        if let Some(context_kind) = context_param_kind(&pat_ty.ty) {
            context_bindings.push(ContextParamBinding {
                ident: ident.clone(),
                kind: context_kind,
            });
        }

        if let Some(custom_transport) = parse_param_transport_mapping(pat_ty)? {
            let transport_ty = &custom_transport.transport_ty;
            let ts_type_str = type_to_ts_name(transport_ty);
            let transport_ident = format_ident!("__{}_transport", ident);
            let actual_ty = &pat_ty.ty;
            params.push(quote! { #[napi(ts_arg_type = #ts_type_str)] #ident: ::serde_json::Value });
            names.push(ident.to_string());
            conversions.push(quote! {
                let #transport_ident: #transport_ty = ::serde_json::from_value(#ident)
                    .map_err(|e| napi::Error::new(napi::Status::InvalidArg, format!("{e}")))?;
                let #ident: #actual_ty = <#actual_ty>::from(#transport_ident);
            });
            call_arg_idents.push(quote! { #ident });
            continue;
        }

        if let Some(permission_kind) = permission_param_kind(&pat_ty.ty)? {
            let guard_ident = permission_guard_ident(ident);
            permission_bindings.push(PermissionParamBinding {
                ty: (*pat_ty.ty).clone(),
                kind: permission_kind,
                guard_ident: guard_ident.clone(),
            });
            call_arg_idents.push(match permission_kind {
                PermissionParamKind::Exclusive => quote! { #guard_ident.write_permission() },
                PermissionParamKind::Shared => quote! { #guard_ident.read_permission() },
            });
            continue;

View on GitHub (pinned to 58e5313667)