gitbutlerapp/gitbutler · error
Failed to list actions: {e}
Error message
Failed to list actions: {e} What it means
anyhow wrapper at crates/but-action/src/action.rs:195 around the paginated SELECT over butler_actions (offset/limit). Rows that fail to convert to ButlerAction are deliberately skipped afterwards (filter_map preserves best-effort listing), so this error means the query itself failed — a locked or unreadable database or a missing table — not malformed rows.
Source
Thrown at crates/but-action/src/action.rs:195
snapshot_before,
snapshot_after,
response,
source,
);
let id = action.id;
persist_action(db, action)?;
Ok(id)
}
/// List persisted Butler actions from `db` using `offset` and `limit` pagination.
///
/// Invalid database rows are skipped to preserve the historical best-effort behavior of the
/// action list endpoint.
pub fn list_actions(db: &DbHandle, offset: i64, limit: i64) -> anyhow::Result<ActionListing> {
let (total, actions) = db
.butler_actions()
.list(offset, limit)
.map_err(|e| anyhow::anyhow!("Failed to list actions: {e}"))?;
// Filter out any entries that cannot be converted to ButlerAction
let actions = actions
.into_iter()
.filter_map(|a| TryInto::try_into(a).ok())
.collect::<Vec<_>>();
Ok(ActionListing { total, actions })
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionListing {
pub total: i64,
pub actions: Vec<ButlerAction>,
}
View on GitHub (pinned to 2497b8007a)
Solutions
- Read {e}: "no such table" means migrations must run; "database is locked" means retry after the concurrent writer finishes
- Upgrade to a binary whose schema matches the project database
- Close concurrent GitButler processes holding the project
- If the database is corrupt, rebuild the project database — action history is best-effort data
Example fix
// before
let (total, actions) = db.butler_actions().list(offset, limit)
.map_err(|e| anyhow::anyhow!("Failed to list actions: {e}"))?;
// after — include the pagination window for faster diagnosis
let (total, actions) = db.butler_actions().list(offset, limit)
.map_err(|e| anyhow::anyhow!("Failed to list actions (offset {offset}, limit {limit}): {e}"))?; Defensive patterns
Strategy: try-catch
Try / catch
match list_actions(&db, offset, limit) {
Ok(listing) => render(listing),
Err(e) => {
log::warn!("action history unavailable: {e:#}");
// do NOT fake an empty history — surface an explicit unavailable state
render_unavailable("Action history is unavailable.");
}
} Prevention
- Open project databases only with binaries that have run current migrations
- Avoid concurrent writers; acquire repository access locks at API boundaries
- Distinguish query failure from an empty history in UI states
- Read the chained cause: "no such table" means migration debt, "locked" means concurrency
When it happens
Trigger: Reading the action history when the butler_actions table does not exist (database created by an older binary without migrations), the database is locked by a concurrent writer, or the file is corrupt or unreadable.
Common situations: Opening a legacy project database without running migrations; the desktop app writing while the CLI lists actions; a truncated database file on a failing disk.
Related errors
- Failed to persist action: {e}
- CliInstallCancelled
- fetch timestamp does not fit in the database: {err}
- Failed to create pull request: {status} - {error_text}
- Failed to verify Bitbucket repository access
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/dfd69085de60cad6.
Report an issue: GitHub.