{"record":{"id":"59726003563d052d","repo":"rust-embedded/rust-raspberrypi-OS-tutorials","slug":"no-handler-registered-for-irq","errorCode":null,"errorMessage":"No handler registered for IRQ {}","messagePattern":"No handler registered for IRQ (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"20_timer_callbacks/kernel/src/bsp/device_driver/arm/gicv2.rs","lineNumber":209,"sourceCode":"    }\n\n    fn handle_pending_irqs<'irq_context>(\n        &'irq_context self,\n        ic: &exception::asynchronous::IRQContext<'irq_context>,\n    ) {\n        // Extract the highest priority pending IRQ number from the Interrupt Acknowledge Register\n        // (IAR).\n        let irq_number = self.gicc.pending_irq_number(ic);\n\n        // Guard against spurious interrupts.\n        if irq_number > GICv2::MAX_IRQ_NUMBER {\n            return;\n        }\n\n        // Call the IRQ handler. Panic if there is none.\n        self.handler_table.read(|table| {\n            match table[irq_number] {\n                None => panic!(\"No handler registered for IRQ {}\", irq_number),\n                Some(descriptor) => {\n                    // Call the IRQ handler. Panics on failure.\n                    descriptor.handler().handle().expect(\"Error handling IRQ\");\n                }\n            }\n        });\n\n        // Signal completion of handling.\n        self.gicc.mark_comleted(irq_number as u32, ic);\n    }\n\n    fn print_handler(&self) {\n        use crate::info;\n\n        info!(\"      Peripheral handler:\");\n\n        self.handler_table.read(|table| {\n            for (i, opt) in table.iter().skip(32).enumerate() {","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials/blob/644474cc09f755249f9c55d99a5d1e07a2562fc7/20_timer_callbacks/kernel/src/bsp/device_driver/arm/gicv2.rs#L191-L227","documentation":"The GICv2 interrupt controller's handle_pending_irqs looks up the pending IRQ number in the registered handler table; if the slot is None, no driver ever registered a handler for that interrupt, so the kernel panics. This library throws it because delivering an IRQ nobody claimed to handle would leave it perpetually asserted (spurious re-fires), which is unrecoverable in this design.","triggerScenarios":"A pending interrupt read from GICD_ISENABLER/GICC IAR has an ID whose entry in the IRQ handler table is None — i.e. the IRQ was enabled at the GIC/distributor level but register_handler() was never called for it.","commonSituations":"Enabling an IRQ in the GIC distributor before registering its handler, BSP interrupt-controller init enabling default IRQs, adding a new device that fires an IRQ but forgetting to register its handler, or IRQ number mismatches between datasheet and code.","solutions":["Call register_handler() with an IRQHandler descriptor for the reported IRQ number before enabling it in the GIC.","Disable the unexpected IRQ at the GIC distributor (GICD_ICENABLER) if it should not fire, or mask it at the peripheral.","Cross-check the IRQ number printed in the panic against the device datasheet and the BSP's defined IRQ numbers.","Add a default/no-op handler for interrupts you intend to ignore but not mask."],"exampleFix":"// before\ninterrupt_controller.irq_manager.enable(irq_number); // enabled but no handler registered\n// after\ninterrupt_controller.irq_manager.register_handler(irq_number, &timer_handler);\ninterrupt_controller.irq_manager.enable(irq_number);","handlingStrategy":"validation","validationCode":"// Before enabling an IRQ, assert a handler is registered:\nfn can_enable(irq: usize, table: &[Option<IrqDescriptor>; N]) -> bool {\n    table.get(irq).map_or(false, |slot| slot.is_some())\n}","typeGuard":"fn has_handler(irq: usize, table: &[Option<IrqDescriptor>]) -> bool {\n    matches!(table.get(irq), Some(Some(_)))\n}","tryCatchPattern":null,"preventionTips":["Register every handler before enabling its IRQ line — make enable() require prior registration.","Keep a single source of truth for IRQ numbers per BSP to avoid numbering mistakes.","Test each IRQ by forcing it to fire during bring-up.","Disable unused IRQ lines at the distributor so they cannot become pending."],"tags":["interrupts","gicv2","aarch64","bare-metal","panic"],"backgroundTag":"missing-irq-handler","analyzedSha":"644474cc09f755249f9c55d99a5d1e07a2562fc7","analyzedAt":"2026-09-06T09:25:56.584Z","contentChangedAt":"2026-09-06T09:25:56.584Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}