AprilNEA/OpenLogi · warning

pass either --percent or --lumens

Error message

pass either --percent or --lumens

What it means

Raised in `set_brightness` when neither `--percent` nor `--lumens` is supplied, so no brightness value can be built. The complementary case (both flags at once) is unreachable because clap enforces the argument conflict at parse time, so this error is the only real missing-argument path.

Solutions

  1. Add either `--percent <0-100>` or `--lumens <value>` to the brightness command
  2. If scripting, default the value explicitly (e.g. `--percent 50`) instead of omitting it
  3. Note that passing both flags is rejected earlier by clap's conflict rule — pick exactly one

Example fix

// before
openlogi light brightness --device "MX Mouse"
// after
openlogi light brightness --device "MX Mouse" --percent 60
Defensive patterns

Strategy: validation

Validate before calling

if args.percent.is_none() && args.lumens.is_none() {
    anyhow::bail!("pass either --percent or --lumens");
}

Type guard

fn has_brightness_value(args: &BrightnessArgs) -> bool {
    args.percent.is_some() ^ args.lumens.is_some()
}

Try / catch

match set_brightness(args).await {
    Ok(()) => (),
    Err(e) if e.to_string().contains("pass either") => {
        eprintln!("Usage: openlogi light brightness --percent <0-100> | --lumens <n>");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Running the brightness subcommand with no value flag at all, e.g. `openlogi light brightness` — clap accepts it (the flags are individually optional) and the command handler bails.

Common situations: Forgetting the flag in scripts; copying a command line and dropping the value; assuming a default brightness exists (there is none by design).

Related errors


AI-assisted analysis of AprilNEA/OpenLogi@e846e6f4b4 (2026-09-13). Data as JSON: /api/errors/0af57ac4c255c27b. Report an issue: GitHub.

Appendix: source

Thrown at crates/openlogi-cli/src/cmd/light.rs:140

        .light_capabilities
        .ok_or_else(|| anyhow!("selected light did not advertise capabilities"))?;
    let range = caps
        .brightness
        .ok_or_else(|| anyhow!("selected light does not support brightness"))?;
    let command = match (args.percent, args.lumens) {
        (Some(percent), None) => LightCommand::BrightnessPercent(percent),
        (None, Some(lumens)) => {
            if range.unit() != LightValueUnit::Lumens || !range.contains(lumens) {
                return Err(anyhow!(
                    "lumens must be in the supported range {}..={} with step {}",
                    range.min(),
                    range.max(),
                    range.step()
                ));
            }
            LightCommand::BrightnessNative(lumens)
        }
        (None, None) => return Err(anyhow!("pass either --percent or --lumens")),
        (Some(_), Some(_)) => unreachable!("clap enforces the argument conflict"),
    };
    apply(device, command).await
}

async fn set_temperature(args: TemperatureArgs) -> Result<()> {
    let devices = standalone().await?;
    let device = select(&devices, args.device.device.as_deref())?;
    apply(device, LightCommand::TemperatureKelvin(args.kelvin)).await
}

async fn apply(device: &StandaloneDevice, command: LightCommand) -> Result<()> {
    let model = find_litra(
        device.address.vendor_id,
        device.address.product_id,
        device.address.usage_page,
        device.address.usage_id,
    )

View on GitHub (pinned to e846e6f4b4)