embassy-rs/embassy · error

Failed to read volume.

Error message

Failed to read volume.

What it means

UAC1 speaker volume-set handling parses the host's volume payload with `i16::from_ne_bytes(data[..2])` and expects the conversion to succeed. The host sent a SET_CUR(VOLUME) control request whose data is shorter than 2 bytes, so the slice-to-array conversion fails. This is a malformed or truncated control transfer payload.

Solutions

  1. Guard the length before parsing: if `data.len() < 2`, return `OutResponse::Rejected` instead of slicing
  2. Use `data.first_chunk::<2>()` and handle None by rejecting the request
  3. Check the descriptor's volume control sizes match what the host expects (b SUBFRAME / feature-unit wMaxClaimed settings) so hosts send 2-byte payloads

Example fix

// before
let volume = i16::from_ne_bytes(data[..2].try_into().expect("Failed to read volume."));
// after
let volume = match data.first_chunk::<2>() {
    Some(b) => i16::from_ne_bytes(*b),
    None => return OutResponse::Rejected,
};
Defensive patterns

Strategy: validation

Validate before calling

if data.len() < 2 { return OutResponse::Rejected; }
let volume = i16::from_ne_bytes(data[..2].try_into().unwrap());

Prevention

When it happens

Trigger: Host issues SET_CUR on the volume control with `data.len() < 2` (empty or 1-byte wValue payload), then `interface_set_volume` panics slicing `data[..2]`.

Common situations: Non-conformant USB audio hosts/drivers sending malformed SET_CUR requests; test tools (e.g. custom control scripts) issuing volume requests with wrong wLength; OS drivers sending per-channel requests with unexpected payload framing.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at embassy-usb/src/class/uac1/speaker.rs:521

                audio_settings.muted[channel_index as usize] = mute_state;
            }
            _ => {
                debug!("Failed to set channel {} mute state: {}", channel_index, mute_state);
                return OutResponse::Rejected;
            }
        }

        debug!("Set channel {} mute state: {}", channel_index, mute_state);
        OutResponse::Accepted
    }

    fn interface_set_volume(
        &mut self,
        audio_settings: &mut AudioSettings,
        channel_index: u8,
        data: &[u8],
    ) -> OutResponse {
        let volume = i16::from_ne_bytes(data[..2].try_into().expect("Failed to read volume."));

        match channel_index as usize {
            ..=MAX_AUDIO_CHANNEL_INDEX => {
                audio_settings.volume_8q8_db[channel_index as usize] = volume;
            }
            _ => {
                debug!("Failed to set channel {} volume: {}", channel_index, volume);
                return OutResponse::Rejected;
            }
        }

        debug!("Set channel {} volume: {}", channel_index, volume);
        OutResponse::Accepted
    }

    fn interface_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
        let interface_number = req.index as u8;
        let entity_index = (req.index >> 8) as u8;

View on GitHub (pinned to 463a07b963)