{"record":{"id":"60e5e95de16fbc88","repo":"gitbutlerapp/gitbutler","slug":"error-when-registering-schema-for-type-registered-at","errorCode":null,"errorMessage":"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","messagePattern":"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","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-ts/src/main.rs","lineNumber":415,"sourceCode":"    // types we actually intend to output.  This avoids false name-collision\n    // errors when unrelated crates happen to register a type with the same\n    // schema name (e.g. two different `Author` types from different crates).\n    if !source_crates.is_empty() {\n        types.retain(|t| {\n            source_crates\n                .iter()\n                .any(|prefix| t.type_name.starts_with(prefix.as_str()))\n        });\n    }\n\n    // Order by registration location for a stable checking order\n    types.sort_by_cached_key(|s| s.registration_location);\n\n    let mut existing_names = HashMap::<&str, &CollectedSchemarEntry>::new();\n\n    for t in &types {\n        if let Some(duplicate) = existing_names.get(t.name.as_ref()) {\n            bail!(\n                \"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\",\n                t.name,\n                t.type_name,\n                t.registration_location,\n                duplicate.type_name,\n                duplicate.registration_location\n            );\n        } else {\n            existing_names.insert(t.name.as_ref(), t);\n        }\n    }\n\n    for t in &types {\n        if let Some(obj) = t.schema.as_object()\n            && let Some(serde_json::Value::Object(defs)) =\n                obj.get(\"$defs\").or_else(|| obj.get(\"definitions\"))\n        {\n            for (def_name, def_schema) in defs {","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-ts/src/main.rs#L397-L433","documentation":"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.","triggerScenarios":"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!()`.","commonSituations":"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.","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."],"exampleFix":"// before\n#[derive(JsonSchema)]\npub struct Branch { pub name: String }\n// after\n#[derive(JsonSchema)]\n#[serde(rename = \"ButlerBranch\")]\n#[schemars(rename = \"ButlerBranch\")]\npub struct Branch { pub name: String }","handlingStrategy":"validation","validationCode":"let names: HashSet<_> = registered.iter().map(|t| t.name.as_str()).collect();\nif names.len() != registered.len() {\n    return Err(\"duplicate schema names found before generating bindings\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["code-generation","schema","naming-collision","build-tooling"],"backgroundTag":"schema-validation-failed","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"}