{"record":{"id":"b7f9738691572772","repo":"BoundaryML/baml","slug":"interface-declares-no-method-for-its-default","errorCode":null,"errorMessage":"interface `{}` declares no method `{}` for its default","messagePattern":"interface `(.+?)` declares no method `(.+?)` for its default","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"baml_language/crates/baml_compiler2_emit/src/lib.rs","lineNumber":1238,"sourceCode":"    method: Name,\n    default: ObjectIndex,\n}\n\n/// Write each back-filled default into its pooled `Object::Interface`.\nfn apply_interface_default_backfill(program: &mut Program, backfill: &[InterfaceDefaultBackfill]) {\n    for entry in backfill {\n        let Object::Interface(iface) = &mut program.objects[entry.interface] else {\n            unreachable!(\n                \"interface index {} is not an Object::Interface\",\n                entry.interface.raw()\n            )\n        };\n        let method = iface\n            .methods\n            .iter_mut()\n            .find(|method| method.name == entry.method)\n            .unwrap_or_else(|| {\n                unreachable!(\n                    \"interface `{}` declares no method `{}` for its default\",\n                    iface.name, entry.method\n                )\n            });\n        method.default = Some(entry.default);\n    }\n}\n\npub(crate) use emit::compile_mir_function;\n\nfn is_builtin_function_name(name: &str) -> bool {\n    matches!(\n        name.split('.').next(),\n        Some(\n            \"baml\"\n                | \"boundary\"\n                | \"reflect\"\n                | \"assert\"","sourceCodeStart":1220,"sourceCodeEnd":1256,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/baml_compiler2_emit/src/lib.rs#L1220-L1256","documentation":"This is a Rust `unreachable!()` panic inside `apply_interface_default_backfill` in baml_compiler2_emit. A deferred backfill entry references a method name that the pooled interface's `methods` list does not contain. The backfill pass assumes any method default it deferred during pooling still exists on the interface when it writes `method.default = Some(entry.default)`. The library panics because this desynchronization indicates a compiler bug, not user error.","triggerScenarios":"Occurs when an `InterfaceDefaultBackfill { interface, method, default }` names a method (`entry.method`) absent from `iface.methods` at backfill time — e.g. the method was renamed, dropped, or never inserted into the interface's method list before backfill runs.","commonSituations":"Compiler development scenarios: renaming interface methods in the parser/AST without updating deferred-default bookkeeping, filtering or pruning interface methods during lowering, or pooling interfaces before all declared methods are attached.","solutions":["Verify every interface method that can carry a `default` is inserted into `iface.methods` before `apply_interface_default_backfill` runs.","Check for renames between default-record time and backfill time; key the backfill by a stable identity (index) rather than `Name` if names can change.","Confirm no pass removes or filters interface methods after backfill entries are created.","If a method default can legitimately target a nonexistent method, replace the `unreachable!` with a diagnostic instead of panicking."],"exampleFix":"// before: default recorded under old method name, method renamed later\nbackfill.push(InterfaceDefaultBackfill { interface, method: Name::from(\"to_string\"), default });\n// after: record after the method list is final, using the final method name\nlet method = iface.methods.iter().find(|m| m.name == final_name).expect(\"method pooled\");\nbackfill.push(InterfaceDefaultBackfill { interface, method: method.name.clone(), default });","handlingStrategy":"validation","validationCode":"// If embedding the compiler, check the deferred default targets an existing method:\nfn default_method_exists(iface: &Interface, method: &Name) -> bool {\n    iface.methods.iter().any(|m| &m.name == method)\n}","typeGuard":"// Not applicable to end users; for compiler code prefer a lookup that returns Option\n// and surface a compile diagnostic instead of panicking:\nfn find_method<'a>(iface: &'a Interface, name: &Name) -> Option<&'a Method> {\n    iface.methods.iter().find(|m| &m.name == name)\n}","tryCatchPattern":"// Panics are not catchable from BAML user code. Embedders can isolate compilation:\nlet result = std::panic::catch_unwind(|| compile(&source));\nif result.is_err() {\n    report_internal_compiler_error();\n}","preventionTips":["Key deferred defaults by stable method identity (index) rather than a renameable `Name`.","Finalize the interface method list before creating backfill entries.","Never prune, filter, or rename interface methods after backfill entries are recorded.","Convert `unreachable!` into a proper diagnostic when a method may legitimately be absent."],"tags":["compiler","internal-invariant","panic","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}