{"record":{"id":"2972b19057e6a5f9","repo":"embassy-rs/embassy","slug":"you-can-only-call-interface-interface-alt-after","errorCode":null,"errorMessage":"you can only call `interface/interface_alt` after `configuration`.","messagePattern":"you can only call `interface/interface_alt` after `configuration`\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-usb/src/descriptor.rs","lineNumber":220,"sourceCode":"    /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices\n    ///   that do not conform to any class.\n    /// * `interface_sub_class` - Sub-class code. Depends on class.\n    /// * `interface_protocol` - Protocol code. Depends on class and sub-class.\n    /// * `interface_string` - Index of string descriptor describing this interface\n    pub fn interface_alt(\n        &mut self,\n        number: InterfaceNumber,\n        alternate_setting: u8,\n        interface_class: u8,\n        interface_sub_class: u8,\n        interface_protocol: u8,\n        interface_string: Option<StringIndex>,\n    ) {\n        if alternate_setting == 0 {\n            match self.num_interfaces_mark {\n                Some(mark) => self.buf[mark] += 1,\n                None => {\n                    panic!(\"you can only call `interface/interface_alt` after `configuration`.\")\n                }\n            };\n        }\n\n        let str_index = interface_string.map_or(0, Into::into);\n\n        self.num_endpoints_mark = Some(self.position + 4);\n\n        self.write(\n            descriptor_type::INTERFACE,\n            &[\n                number.into(),       // bInterfaceNumber\n                alternate_setting,   // bAlternateSetting\n                0,                   // bNumEndpoints\n                interface_class,     // bInterfaceClass\n                interface_sub_class, // bInterfaceSubClass\n                interface_protocol,  // bInterfaceProtocol\n                str_index,           // iInterface","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-usb/src/descriptor.rs#L202-L238","documentation":"embassy-usb's DescriptorWriter tracks the count of interfaces in the current USB configuration via a mark placed by `configuration()`. Calling `interface()` or `interface_alt()` before any configuration is active means there is no bNumInterfaces counter to increment, which is an invalid descriptor-building sequence, so the writer panics instead of emitting a corrupt descriptor.","triggerScenarios":"Calling `DescriptorWriter::interface()` or `interface_alt()` outside the closure passed to `UsbDeviceBuilder`/`UsbDevice`'s descriptor-building path, i.e. without a preceding `configuration()` call on the same writer.","commonSituations":"Hand-writing descriptor code in a custom `UsbClass`/handler and forgetting the `config.configuration(...)` wrapper; reordering calls so interfaces are written before the configuration descriptor; copying example code but moving `interface()` calls outside the configuration block.","solutions":["Wrap all `interface`/`endpoint` calls inside the `configuration()` block (e.g. within your class's `get_configuration_descriptors` implementation).","Ensure you implement `UsbClass::get_configuration_descriptors` rather than writing interfaces into a fresh/bare DescriptorWriter.","Check call ordering: `configuration()` must run first, then one or more `interface`/`interface_alt`, then `endpoint` calls.","Update to the latest embassy-usb version in case the API shape changed and your code targets an older pattern."],"exampleFix":"// before\nfn get_configuration_descriptors(&self, w: &mut DescriptorWriter) {\n    w.interface(0, 0, 2, 0xFF, 0, 0, None);\n}\n// after\nfn get_configuration_descriptors(&self, w: &mut DescriptorWriter) {\n    w.configuration(1)?; // activate the configuration first\n    w.interface(0, 0, 2, 0xFF, 0, 0, None)?;\n    w.endpoint(...)?;\n    w.end_interfaces();\n    w.end_configuration();\n    Ok(())\n}","handlingStrategy":"validation","validationCode":"// Only write interface descriptors inside the configuration callback:\nfn get_configuration_descriptors(&self, w: &mut DescriptorWriter) -> Result<(), DescriptorError> {\n    w.configuration(1)?; // must precede any interface() call\n    w.interface(0, 0, 2, 0xFF, 0, 0, None)?;\n    w.end_interfaces();\n    w.end_configuration();\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always open a configuration with w.configuration() before any interface/interface_alt call","Only emit interface/endpoint descriptors inside get_configuration_descriptors","Keep a strict ordering: configuration -> interface -> endpoint(s) -> end_interfaces -> end_configuration","Unit-test custom UsbClass descriptor building with an in-memory writer"],"tags":["usb","embedded","panic","descriptor-builder"],"backgroundTag":"invalid-state-transition","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}