{"record":{"id":"3c42532d53bcd67f","repo":"gitbutlerapp/gitbutler","slug":"invalid-e","errorCode":null,"errorMessage":"invalid '{}': {e}","messagePattern":"invalid '(.+?)': (.+?)","errorType":"validation","errorClass":"napi::Error","httpStatus":null,"severity":"error","filePath":"crates/but-api-macros/src/lib.rs","lineNumber":1558,"sourceCode":"                    let #ident: gix::refs::FullName = gix::refs::FullName::try_from(#param_name)\n                        .map_err(|e: gix::refs::name::Error| napi::Error::new(napi::Status::InvalidArg, format!(\"{e}\")))?;\n                });\n                let call_ident = match &*pat_ty.ty {\n                    syn::Type::Reference(_) => quote! { #ident.as_ref() },\n                    _ => quote! { #ident },\n                };\n                call_arg_idents.push(call_ident);\n            } else if *last_ident == \"Option\"\n                && is_hex_hash_container_path(&mapping.json_ty, \"Option\")\n            {\n                // Option<ObjectId> via Option<HexHash> → Option<String>, then parse when present.\n                params.push(quote! { #param_name: Option<String> });\n                names.push(param_name.to_string());\n                conversions.push(quote! {\n                    let #ident: Option<gix::ObjectId> = #param_name\n                        .map(|value| {\n                            ::std::str::FromStr::from_str(&value).map_err(|e: gix::hash::decode::Error| {\n                                napi::Error::new(\n                                    napi::Status::InvalidArg,\n                                    format!(\"invalid '{}': {e}\", stringify!(#param_name)),\n                                )\n                            })\n                        })\n                        .transpose()?;\n                });\n                call_arg_idents.push(quote! { #ident });\n            } else if *last_ident == \"Vec\" && is_hex_hash_container_path(&mapping.json_ty, \"Vec\") {\n                // Vec<ObjectId> via Vec<HexHash> → Vec<String>, then parse each entry\n                params.push(quote! { #param_name: Vec<String> });\n                names.push(param_name.to_string());\n                conversions.push(quote! {\n                    let #ident: Vec<gix::ObjectId> = #param_name\n                        .into_iter()\n                        .enumerate()\n                        .map(|(index, value)| {\n                            ::std::str::FromStr::from_str(&value).map_err(|e: gix::hash::decode::Error| {","sourceCodeStart":1540,"sourceCodeEnd":1576,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api-macros/src/lib.rs#L1540-L1576","documentation":"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.","triggerScenarios":"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.","commonSituations":"Optional ids defaulted to `\"\"` or `\"null\"` strings instead of undefined; short hashes from UI selections; stale cached ids with corrupted characters.","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."],"exampleFix":"// before\nawait api.getLog(baseId ?? \"\"); // empty string fails decode\n// after\nawait api.getLog(baseId ?? undefined);","handlingStrategy":"type-guard","validationCode":"const HASH_RE = /^[0-9a-f]{40}$/;\nfunction toOptionalObjectId(id) {\n  if (id === undefined || id === null) return undefined; // let the API see \"absent\"\n  if (typeof id !== 'string' || !HASH_RE.test(id)) {\n    throw new TypeError(`invalid commit id: ${id}`);\n  }\n  return id;\n}","typeGuard":"function isPresentObjectId(v) { return v !== undefined && v !== null && /^[0-9a-f]{40}$/.test(v); }","tryCatchPattern":"try { await api.getLog(idOpt); }\ncatch (e) {\n  const m = /invalid '([^']+)':/.exec(e?.message ?? '');\n  if (m) throw new Error(`argument ${m[1]} is not a valid object id`);\n  throw e;\n}","preventionTips":["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."],"tags":["git","napi","hash","optional-parameter"],"backgroundTag":"invalid-identifier-format","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}