{"record":{"id":"054ab10b334ad12b","repo":"BoundaryML/baml","slug":"interface-index-is-not-an-object-interface","errorCode":null,"errorMessage":"interface index {} is not an Object::Interface","messagePattern":"interface index (.+?) is not an Object::Interface","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"baml_language/crates/baml_compiler2_emit/src/lib.rs","lineNumber":1228,"sourceCode":"        pkg.canonicalize_impl_rules();\n    }\n    interface_default_backfill\n}\n\n/// One `InterfaceMethodDef::default` slot `build_packages` could not fill when\n/// the interface was pooled (functions are pooled later), keyed by the pooled\n/// interface and the method's name.\nstruct InterfaceDefaultBackfill {\n    interface: ObjectIndex,\n    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","sourceCodeStart":1210,"sourceCodeEnd":1246,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/baml_compiler2_emit/src/lib.rs#L1210-L1246","documentation":"This is a Rust `unreachable!()` panic inside `apply_interface_default_backfill` in baml_compiler2_emit. The compiler recorded an `InterfaceDefaultBackfill` entry pointing at an object-pool slot that, when written back, no longer holds an `Object::Interface`. It means the object pool was mutated or the recorded `ObjectIndex` was stale/reused between the point the backfill entry was created (in `build_packages`) and the point it was applied. The library panics because this is an internal invariant that should be impossible; it is never a user-input error.","triggerScenarios":"Only hit during compilation when the deferred interface-method-default backfill pass runs with an `InterfaceDefaultBackfill` whose `interface: ObjectIndex` resolves to an object that is not `Object::Interface` — e.g. the pool slot was overwritten by another pooled object (class/enum/type-alias) or the index was built against a different pool ordering.","commonSituations":"Compiler development, not end-user BAML code: refactoring object pooling in baml_compiler2_emit, changing pooled-object ordering between pass phases, or reusing/shuffling ObjectIndex slots without invalidating backfill entries.","solutions":["Audit the code path that pushes to `interface_default_backfill` and confirm the recorded ObjectIndex is created after final pool assignment and never invalidated.","Check whether any pass between backfill entry creation and `apply_interface_default_backfill` mutates `program.objects` (push, remove, or overwrite slots).","Ensure `apply_interface_default_backfill` runs in the same compilation run/ordering as `generate_impl` and pool construction; no pool rebuild may occur in between.","If triggered by an incremental/recompilation path, verify pooled object indices are rebuilt consistently and backfill entries are cleared when the pool is rebuilt."],"exampleFix":"// before: backfill entries recorded before final pooling, indices go stale\nlet backfill = collect_backfill(&half_built_pool);\nrebuild_pool(program);\napply_interface_default_backfill(program, &backfill);\n// after: record backfill entries only after the pool is final\nlet backfill = collect_backfill(program); // pool final; indices stable\napply_interface_default_backfill(program, &backfill);","handlingStrategy":"validation","validationCode":"// User code cannot pre-validate a compiler-internal invariant. If embedding the compiler,\n// verify the pool slot before applying backfill:\nfn backfill_target_is_interface(program: &Program, idx: ObjectIndex) -> bool {\n    matches!(program.objects.get(idx), Some(Object::Interface(_)))\n}","typeGuard":"fn is_interface(obj: &Object) -> bool {\n    matches!(obj, Object::Interface(_))\n}","tryCatchPattern":"// Rust panics are not catchable in user BAML code; when embedding the compiler, isolate\n// the compile call and recover:\nlet result = std::panic::catch_unwind(|| compile(&source));\nmatch result {\n    Ok(artifacts) => artifacts,\n    Err(_) => report_internal_compiler_error(),\n}","preventionTips":["Record backfill entries only after the object pool reaches its final ordering.","Never rebuild or reorder `program.objects` between backfill collection and application.","Clear deferred backfill state whenever the pool is rebuilt or invalidated.","Add debug assertions validating backfill targets immediately at record time."],"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"}