gitbutlerapp/gitbutler · error

Unknown app suffix '{value}', expected one of: dev, nightly,

Error message

Unknown app suffix '{value}', expected one of: dev, nightly, release

What it means

`AppChannel::from_str` rejects any value outside the accepted set: `nightly`, `release`/`production`/`prod`, and `dev`/`development` (case-sensitive). It exists so channel identifiers parsed from CLI args, env vars, or config reject unknown suffixes instead of silently defaulting. The error lists the canonical accepted values.

Source

Thrown at crates/but-path/src/lib.rs:339

    new_window: bool,
) -> anyhow::Result<url::Url> {
    let mut url = url::Url::parse(&format!("{scheme}://open"))?;
    url.query_pairs_mut()
        .append_pair("path", &possibly_project_dir.to_string_lossy())
        .append_pair("t", &timestamp.to_string())
        .append_pair("new_window", if new_window { "1" } else { "0" });
    Ok(url)
}

impl std::str::FromStr for AppChannel {
    type Err = anyhow::Error;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "nightly" => Ok(AppChannel::Nightly),
            "release" | "production" | "prod" => Ok(AppChannel::Release),
            "dev" | "development" => Ok(AppChannel::Dev),
            _ => Err(anyhow::anyhow!(
                "Unknown app suffix '{value}', expected one of: dev, nightly, release"
            )),
        }
    }
}

fn clean_env_vars<'a, 'b>(
    var_names: &'a [&'b str],
) -> impl Iterator<Item = (&'b str, String)> + 'a {
    var_names
        .iter()
        .filter_map(|name| env::var(name).map(|value| (*name, value)).ok())
        .map(|(name, value)| {
            (
                name,
                value
                    .split(':')
                    .filter(|path| !path.contains("appimage-run") && !path.contains("/tmp/.mount"))

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Use one of: dev, nightly, release (aliases: development, production, prod)
  2. Trim and lowercase the input before parsing
  3. If unknown channels should fall back, match explicitly first and default to `AppChannel::Release` instead of parsing blindly

Example fix

// before
let channel: AppChannel = arg.parse()?; // Unknown app suffix 'stable'

// after
let channel = match arg.trim().to_ascii_lowercase().as_str() {
    "nightly" => AppChannel::Nightly,
    "dev" | "development" => AppChannel::Dev,
    "release" | "production" | "prod" | _ => AppChannel::Release,
};
Defensive patterns

Strategy: validation

Validate before calling

const VALID_CHANNELS: &[&str] = &["dev", "development", "nightly", "release", "production", "prod"];
anyhow::ensure!(VALID_CHANNELS.contains(&value.trim()), "invalid channel {value}");
let channel: AppChannel = value.trim().parse()?;

Type guard

fn is_valid_app_channel(value: &str) -> bool {
    matches!(
        value.trim(),
        "dev" | "development" | "nightly" | "release" | "production" | "prod"
    )
}

Prevention

When it happens

Trigger: Parsing "stable", "beta", "Release" (wrong case), or "production " (whitespace) from `--channel`-style args, environment variables, or serialized config into `AppChannel`.

Common situations: Scripts passing marketing channel names ("stable") that don't map to build channels; config written for a newer/older version with renamed channels; casing or trailing-newline mistakes when piping values from files.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/724ba35d45eaea62. Report an issue: GitHub.