embassy-rs/embassy · error

called `capability` not between `bos` and `end_bos`.

Error message

called `capability` not between `bos` and `end_bos`.

What it means

`capability()` appends a BOS device-capability descriptor and increments the bNumDeviceCaps counter whose location was recorded by `bos()`. If `capability()` is invoked when no BOS container is open, the counter mark is None and the writer panics rather than produce a malformed BOS descriptor.

Solutions

  1. Call `capability(...)` only between `writer.bos()` and `writer.end_bos()`.
  2. Move capability writes into the BOS-descriptor path of your class implementation.
  3. Ensure `bos()`/`end_bos()` are called exactly once as an enclosing pair.
  4. Refactor custom classes so capability registration goes through the proper hook that opens the BOS writer.

Example fix

// before
fn get_configuration_descriptors(&self, w: &mut DescriptorWriter) {
    w.capability(0x06, &cap_data)?; // outside BOS
}
// after
fn get_bos_descriptors(&self, w: &mut BosWriter) {
    w.bos();
    w.capability(0x06, &cap_data)?;
    w.end_bos();
    Ok(())
}
Defensive patterns

Strategy: validation

Validate before calling

fn get_bos_descriptors(&self, w: &mut BosWriter) -> Result<(), DescriptorError> {
    w.bos();               // opens the BOS container
    w.capability(CAP_TYPE_WEBUSB, &CAP_DATA)?; // only valid here
    w.end_bos();
    Ok(())
}

Prevention

When it happens

Trigger: Calling `DescriptorWriter::capability()` outside the span between `bos()` and `end_bos()` calls on the same writer.

Common situations: Adding USB capabilities (e.g. WebUSB, Microsoft OS 2.0) in the wrong descriptor callback; forgetting the `end_bos()`/`bos()` pair when restructuring custom descriptor code; calling capability during configuration-descriptor building instead of BOS building.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/c1f20888e189d3ca. Report an issue: GitHub.

Appendix: source

Thrown at embassy-usb/src/descriptor.rs:432

                0x00, 0x00, // wTotalLength
                0x00, // bNumDeviceCaps
            ],
            &[],
        );

        self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]);
    }

    /// Writes capability descriptor to a BOS
    ///
    /// # Arguments
    ///
    /// * `capability_type` - Type of a capability
    /// * `data` - Binary data of the descriptor
    pub fn capability(&mut self, capability_type: u8, data: &[u8]) {
        match self.num_caps_mark {
            Some(mark) => self.writer.buf[mark] += 1,
            None => panic!("called `capability` not between `bos` and `end_bos`."),
        }

        let mut start = self.writer.position;
        let blen = data.len();

        assert!(
            (start + blen + 3) <= self.writer.buf.len() && (blen + 3) <= 255,
            "Descriptor buffer full"
        );

        self.writer.buf[start] = (blen + 3) as u8;
        self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
        self.writer.buf[start + 2] = capability_type;

        start += 3;
        self.writer.buf[start..start + blen].copy_from_slice(data);
        self.writer.position = start + blen;
    }

View on GitHub (pinned to 463a07b963)