gitbutlerapp/gitbutler · error

Invalid Handler value

Error message

Invalid Handler value

What it means

The From/TryInto conversion building a ButlerAction from its persisted representation parses the handler string into the Handler type. An unparseable handler value makes the conversion fail with 'Invalid Handler value', guarding against corrupt or unknown action records in the database.

Solutions

  1. Check the handler string stored in the butler_actions row and correct it to a valid Handler variant name
  2. Upgrade/downgrade to the app version that wrote the record so the handler parses
  3. Skip or delete the offending rows (list_actions already filters such rows) to keep the app working
  4. If reproducible, extend Handler::from_str to accept the new handler spelling
Defensive patterns

Strategy: type-guard

Validate before calling

// treat persisted rows as fallible
let action: Option<ButlerAction> = row.try_into().ok(); // skip invalid rows like list_actions does

Type guard

fn is_valid_handler(h: &str) -> bool {
    h.parse::<Handler>().is_ok()
}

Try / catch

match ButlerAction::try_from(row) {
  Ok(a) => handle(a),
  Err(e) if e.to_string().contains("Invalid Handler value") => {
    log::warn!("skipping action with unknown handler"); // best-effort
  }
  Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Reading a butler_actions row whose handler column contains a string that Handler::from_str (parse()) rejects — unknown handler name, empty string, or hand-edited/corrupt db content.

Common situations: Schema/version drift: an older or newer build wrote handler names this build doesn't know; manual DB edits; corrupted rows after a crash; copying db files between app versions.

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@58e5313667 (2026-09-18). Data as JSON: /api/errors/2bfa822199bb3485. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-action/src/action.rs:84

    fn try_from(value: but_db::ButlerAction) -> Result<Self, Self::Error> {
        let response = value
            .response
            .as_ref()
            .and_then(|o| serde_json::from_str(o).ok());
        let source = value
            .source
            .as_deref()
            .and_then(|s| serde_json::from_str(s).ok())
            .unwrap_or_default();
        Ok(Self {
            id: Uuid::parse_str(&value.id)?,
            created_at: value.created_at,
            external_prompt: value.external_prompt,
            external_summary: value.external_summary,
            handler: value
                .handler
                .parse()
                .map_err(|_| anyhow::anyhow!("Invalid Handler value"))?,
            snapshot_before: gix::ObjectId::from_str(&value.snapshot_before)?,
            snapshot_after: gix::ObjectId::from_str(&value.snapshot_after)?,
            response,
            error: value.error,
            source,
        })
    }
}

impl TryFrom<ButlerAction> for but_db::ButlerAction {
    type Error = anyhow::Error;

    fn try_from(value: ButlerAction) -> Result<Self, Self::Error> {
        let response = value
            .response
            .as_ref()
            .and_then(|o| serde_json::to_string(o).ok());
        let source = serde_json::to_string(&value.source).ok();

View on GitHub (pinned to 58e5313667)