gitbutlerapp/gitbutler · error · napi::Error
invalid
Error message
invalid '{}': {e} What it means
Generated in `build_napi_params` for optional ObjectId parameters (`Option<gix::ObjectId>` taken as `Option<String>`): when a value is provided, `FromStr` into `gix::ObjectId` fails with `gix::hash::decode::Error` and the error message is prefixed `invalid '<paramName>': {e}` as napi InvalidArg. Like error 867 but for optional parameters and naming the offending parameter.
Solutions
- Pass undefined/null for the parameter when there is no id, rather than an empty or placeholder string.
- Supply the full 40-char lowercase hex object id when a value is present.
- Resolve short hashes to full ids before the call.
- Use the parameter name in the message (`invalid '<name>': ...`) to locate exactly which argument is malformed.
Example fix
// before await api.getLog(baseId ?? ""); // empty string fails decode // after await api.getLog(baseId ?? undefined);
Defensive patterns
Strategy: type-guard
Validate before calling
const HASH_RE = /^[0-9a-f]{40}$/;
function toOptionalObjectId(id) {
if (id === undefined || id === null) return undefined; // let the API see "absent"
if (typeof id !== 'string' || !HASH_RE.test(id)) {
throw new TypeError(`invalid commit id: ${id}`);
}
return id;
} Type guard
function isPresentObjectId(v) { return v !== undefined && v !== null && /^[0-9a-f]{40}$/.test(v); } Try / catch
try { await api.getLog(idOpt); }
catch (e) {
const m = /invalid '([^']+)':/.exec(e?.message ?? '');
if (m) throw new Error(`argument ${m[1]} is not a valid object id`);
throw e;
} Prevention
- Coerce empty strings to undefined for optional id parameters.
- Resolve short hashes to full 40-char ids before passing.
- Use the `invalid '<param>':` message prefix to pinpoint the offending argument.
When it happens
Trigger: Calling a `but_api` function with an optional commit/id argument set to a non-hex, wrong-length, or otherwise undecodable string instead of null/undefined.
Common situations: Optional ids defaulted to `""` or `"null"` strings instead of undefined; short hashes from UI selections; stale cached ids with corrupted characters.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Aborting due to empty branch name
- Ad-hoc (single-branch) branch moves are not supported…
- An octopus merge commits must have at least two parents
- another pre-commit hook is already using the repository…
- argument ' ' must be a non-negative integer that fits in…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/3c42532d53bcd67f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-api-macros/src/lib.rs:1558
let #ident: gix::refs::FullName = gix::refs::FullName::try_from(#param_name)
.map_err(|e: gix::refs::name::Error| napi::Error::new(napi::Status::InvalidArg, format!("{e}")))?;
});
let call_ident = match &*pat_ty.ty {
syn::Type::Reference(_) => quote! { #ident.as_ref() },
_ => quote! { #ident },
};
call_arg_idents.push(call_ident);
} else if *last_ident == "Option"
&& is_hex_hash_container_path(&mapping.json_ty, "Option")
{
// Option<ObjectId> via Option<HexHash> → Option<String>, then parse when present.
params.push(quote! { #param_name: Option<String> });
names.push(param_name.to_string());
conversions.push(quote! {
let #ident: Option<gix::ObjectId> = #param_name
.map(|value| {
::std::str::FromStr::from_str(&value).map_err(|e: gix::hash::decode::Error| {
napi::Error::new(
napi::Status::InvalidArg,
format!("invalid '{}': {e}", stringify!(#param_name)),
)
})
})
.transpose()?;
});
call_arg_idents.push(quote! { #ident });
} else if *last_ident == "Vec" && is_hex_hash_container_path(&mapping.json_ty, "Vec") {
// Vec<ObjectId> via Vec<HexHash> → Vec<String>, then parse each entry
params.push(quote! { #param_name: Vec<String> });
names.push(param_name.to_string());
conversions.push(quote! {
let #ident: Vec<gix::ObjectId> = #param_name
.into_iter()
.enumerate()
.map(|(index, value)| {
::std::str::FromStr::from_str(&value).map_err(|e: gix::hash::decode::Error| {View on GitHub (pinned to 58e5313667)