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
- Check the handler string stored in the butler_actions row and correct it to a valid Handler variant name
- Upgrade/downgrade to the app version that wrote the record so the handler parses
- Skip or delete the offending rows (list_actions already filters such rows) to keep the app working
- 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
- Never hand-edit the butler_actions table
- Keep app versions consistent with the db they read
- Wrap row conversions in ok()/filter_map for best-effort listing
- Extend Handler parsing when adding new handler names
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
- Failed to serialize hunk_header
- Aborting due to empty PR title
- assignments_error: assignments_error.clone()…
- assignments_error.map(|err| serde_error::Error::new(&*err))
- BUG: Sensitive data cannot be serialized - it needs to be…
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)