BigPizzaV3/CodexPlusPlus · error · anyhow::Error

restore conflict: agent_job_items row already assigned

Error message

restore conflict: agent_job_items row already assigned

What it means

Thrown by detect_restore_conflicts's agent_job_items check (surfaced via restore_rows) when a backed-up agent_job_items row's id is already assigned to a different thread (assigned_thread_id is set and differs). Job items are single-owner; the offending input is the conflicting assignment in the live agent_job_items table, and the restore is refused rather than reassigning the job.

Source

Thrown at crates/codex-plus-data/src/storage.rs:1174

    let Some(id) = row.get("id") else {
        return Ok(false);
    };
    if !row.contains_key("assigned_thread_id") || !has_table(db, "agent_job_items")? {
        return Ok(false);
    }
    let id_value = OwnedSqlValue(json_to_sql_value(id));
    let current_assignment = db.query_row(
        "SELECT assigned_thread_id FROM agent_job_items WHERE id = ?1 LIMIT 1",
        [&id_value as &dyn ToSql],
        |row| row.get::<_, Option<String>>(0),
    );
    let current_assignment = match current_assignment {
        Ok(value) => value,
        Err(rusqlite::Error::QueryReturnedNoRows) => return Ok(false),
        Err(err) => return Err(err.into()),
    };
    if current_assignment.is_some() {
        anyhow::bail!("restore conflict: agent_job_items row already assigned");
    }
    let assigned = OwnedSqlValue(json_to_sql_value(&row["assigned_thread_id"]));
    db.execute(
        "UPDATE agent_job_items SET assigned_thread_id = ?1 WHERE id = ?2 AND assigned_thread_id IS NULL",
        [&assigned as &dyn ToSql, &id_value as &dyn ToSql],
    )?;
    Ok(true)
}

fn backup_related_rows(
    db: &Connection,
    tables: &mut Map<String, Value>,
    table: &str,
    where_clause: &str,
    params: &[&dyn ToSql],
) -> anyhow::Result<()> {
    if has_table(db, table)? {
        let rows = select_dicts(

View on GitHub (pinned to f2074595a2)

Solutions

  1. 确认该 job 条目当前是否已被新会话使用,若是则放弃恢复该行
  2. 先解除现有 assigned_thread_id 赋值再执行恢复
  3. 检查是否重复消费了同一 undo token
  4. 对 agent_job_items 实现按行跳过而非整体失败的恢复策略
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/codex-plus-data/src/storage.rs:1174 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@f2074595a2 (2026-08-23). Data as JSON: /api/errors/e970c1231dcfaf39. Report an issue: GitHub.