gitbutlerapp/gitbutler · error
Error when registering schema
Error message
Error when registering schema {} for type {} registered at {}
Duplicate type {} was registered at {}. Consider renaming one of the types with either `#[serde(rename = ...)]` or `#[schemars(rename = ...)]`.
What it means
During TypeScript binding generation, `collect_all_schemas` in but-ts registers schemas for all types collected from the source crates. Two distinct types ended up with the same registered schema name (typically a struct/enum name), which would produce conflicting `$ref` entries in the generated schema. The tool aborts with `bail!` and points at both registration locations.
Solutions
- Add `#[serde(rename = "UniqueName")]` and/or `#[schemars(rename = "UniqueName")]` to one of the conflicting types so the registered names differ.
- Un-register the duplicate type if it was added by mistake (remove its `register_sdk_type!()` call).
- Check the two `registration_location` paths in the message to find both declarations and decide which one to rename.
Example fix
// before
#[derive(JsonSchema)]
pub struct Branch { pub name: String }
// after
#[derive(JsonSchema)]
#[serde(rename = "ButlerBranch")]
#[schemars(rename = "ButlerBranch")]
pub struct Branch { pub name: String } Defensive patterns
Strategy: validation
Validate before calling
let names: HashSet<_> = registered.iter().map(|t| t.name.as_str()).collect();
if names.len() != registered.len() {
return Err("duplicate schema names found before generating bindings");
} Prevention
- Keep SDK type names unique across modules registered by but-ts.
- Add a CI step that runs `but-ts` so collisions fail the build early.
- Use `#[schemars(rename)]` immediately when two same-named types must coexist.
When it happens
Trigger: Running `but-ts` when two types in the source crates (or a type and a dependent definition) share the same `#[schemars]`/derived name, e.g. two structs named `Branch` in different modules that are both registered via `register_sdk_type!()`.
Common situations: Renaming a module and keeping a same-named struct; adding a new SDK type without noticing an existing registration with the same Rust type name; generating across multiple `--source-crates` where names collide.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Error when registering schema
- Error when registering schema
- existing GitMeta key
- existing GitMeta key
- existing GitMeta key
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/60e5e95de16fbc88.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-ts/src/main.rs:415
// types we actually intend to output. This avoids false name-collision
// errors when unrelated crates happen to register a type with the same
// schema name (e.g. two different `Author` types from different crates).
if !source_crates.is_empty() {
types.retain(|t| {
source_crates
.iter()
.any(|prefix| t.type_name.starts_with(prefix.as_str()))
});
}
// Order by registration location for a stable checking order
types.sort_by_cached_key(|s| s.registration_location);
let mut existing_names = HashMap::<&str, &CollectedSchemarEntry>::new();
for t in &types {
if let Some(duplicate) = existing_names.get(t.name.as_ref()) {
bail!(
"Error when registering schema {} for type {} registered at {}\n\nDuplicate type {} was registered at {}. Consider renaming one of the types with either `#[serde(rename = ...)]` or `#[schemars(rename = ...)]`.\n",
t.name,
t.type_name,
t.registration_location,
duplicate.type_name,
duplicate.registration_location
);
} else {
existing_names.insert(t.name.as_ref(), t);
}
}
for t in &types {
if let Some(obj) = t.schema.as_object()
&& let Some(serde_json::Value::Object(defs)) =
obj.get("$defs").or_else(|| obj.get("definitions"))
{
for (def_name, def_schema) in defs {View on GitHub (pinned to 58e5313667)