BloopAI/vibe-kanban · error

Executor action is not a valid ExecutorAction JSON object

Error message

Executor action is not a valid ExecutorAction JSON object

What it means

An ExecutionProcess row stores its executor action as JSON that is expected to deserialize into an ExecutorAction. This accessor returns Err when the stored JSON is the 'Other' variant — i.e. the payload is valid JSON but not a recognized ExecutorAction shape (unknown/legacy executor payload, or an externally-written row).

Source

Thrown at crates/db/src/models/execution_process.rs:461

        sqlx::query!(
            r#"UPDATE execution_processes
               SET status = $1, exit_code = $2, completed_at = $3
               WHERE id = $4"#,
            status,
            exit_code,
            completed_at,
            id
        )
        .execute(pool)
        .await?;

        Ok(())
    }

    pub fn executor_action(&self) -> Result<&ExecutorAction, anyhow::Error> {
        match &self.executor_action.0 {
            ExecutorActionField::ExecutorAction(action) => Ok(action),
            ExecutorActionField::Other(_) => Err(anyhow::anyhow!(
                "Executor action is not a valid ExecutorAction JSON object"
            )),
        }
    }

    /// Soft-drop processes at and after the specified boundary (inclusive)
    pub async fn drop_at_and_after(
        pool: &SqlitePool,
        session_id: Uuid,
        boundary_process_id: Uuid,
    ) -> Result<i64, sqlx::Error> {
        let result = sqlx::query!(
            r#"UPDATE execution_processes
               SET dropped = TRUE
             WHERE session_id = $1
               AND created_at >= (SELECT created_at FROM execution_processes WHERE id = $2)
               AND dropped = FALSE"#,
            session_id,

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Inspect the row's executor_action JSON and re-write it as a valid ExecutorAction object matching the current schema.
  2. Check whether the row predates an executor schema change; re-run the task/process so a fresh row is created with the current format.
  3. If the payload is intentionally unsupported, handle the Err case and skip/derive actions from alternative columns instead of unwrapping.
  4. Add a DB migration or backfill script that converts legacy 'Other' payloads to the current ExecutorAction format.

Example fix

// before
let action = process.executor_action()?;
// after
let action = match process.executor_action() {
    Ok(a) => a,
    Err(_) => { tracing::warn!(id = %process.id, "unsupported executor action payload"); continue; }
};
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_supported_executor_action(json: &serde_json::Value) -> bool {
    json.get("type").and_then(|t| t.as_str())
        .map(|t| matches!(t, "coding-agent" | "script"))
        .unwrap_or(false)
}

Type guard

fn as_executor_action(
    process: &ExecutionProcess,
) -> Option<&ExecutorAction> {
    process.executor_action().ok()
}

Try / catch

let action = match process.executor_action() {
    Ok(a) => a,
    Err(e) => {
        tracing::warn!(process_id = %process.id, error = %e, "skipping legacy executor payload");
        return Ok(None);
    }
};

Prevention

When it happens

Trigger: Calling execution_process.executor_action() on a row whose executor_action JSON column parsed as ExecutorActionField::Other instead of ExecutorActionField::ExecutorAction.

Common situations: Database rows written by an older version of the app before a ts-rs/schema change, manual DB edits or migrations inserting raw JSON, or rows created by a different executor plugin not known to the current enum.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/642776f14ead3dc5. Report an issue: GitHub.