affaan-m/ECC · error

Scheduled task was not found after insert

Error message

Scheduled task {id} was not found after insert

What it means

After INSERTing a scheduled task, the store reads last_insert_rowid() and immediately re-fetches the row via get_scheduled_task. If that read returns None, the row written moments earlier cannot be read back, so the store raises `Scheduled task {id} was not found after insert`. It is a post-insert readback invariant check.

Solutions

  1. Check for concurrent deletions of scheduled tasks between insert and readback.
  2. Ensure all statements on the connection use the same thread/serialized access so last_insert_rowid is valid.
  3. Wrap insert + readback in a transaction to make the invariant atomic.
  4. Enable SQLite foreign-key/trigger logging to see if a trigger removed the row.

Example fix

// before
let id = self.conn.last_insert_rowid();
self.get_scheduled_task(id)?.ok_or_else(|| anyhow::anyhow!("Scheduled task {id} was not found after insert"))
// after
self.conn.execute("BEGIN IMMEDIATE", [])?;
// ... insert ...
let id = self.conn.last_insert_rowid();
let task = self.get_scheduled_task(id)?;
self.conn.execute("COMMIT", [])?;
task.ok_or_else(|| anyhow::anyhow!("Scheduled task {id} was not found after insert"))
Defensive patterns

Strategy: try-catch

Try / catch

match result {
    Err(e) if e.to_string().contains("was not found after insert") => {
        // rowid race: retry the insert once inside a transaction
        retry_in_transaction(|| insert_and_fetch())?;
    }
    Err(e) => return Err(e),
    Ok(task) => {},
}

Prevention

When it happens

Trigger: INSERT succeeds, last_insert_rowid() returns an id, but get_scheduled_task(id) finds no row — e.g. another connection deleted the row between insert and read, last_insert_rowid() returning a stale id from a different table because the INSERT silently failed to insert, or a trigger/constraint anomaly.

Common situations: Concurrent cleanup jobs deleting scheduled tasks; sharing one connection where last_insert_rowid reflects another statement; SQLite WAL contention; misuse of the same conn from multiple threads.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/3a825384b37bc826. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/session/store.rs:1537

                updated_at
             ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)",
            rusqlite::params![
                cron_expr,
                task,
                agent_type,
                profile_name,
                working_dir.display().to_string(),
                project,
                task_group,
                if use_worktree { 1_i64 } else { 0_i64 },
                next_run_at.to_rfc3339(),
                now.to_rfc3339(),
                now.to_rfc3339(),
            ],
        )?;
        let id = self.conn.last_insert_rowid();
        self.get_scheduled_task(id)?
            .ok_or_else(|| anyhow::anyhow!("Scheduled task {id} was not found after insert"))
    }

    pub fn list_scheduled_tasks(&self) -> Result<Vec<ScheduledTask>> {
        let mut stmt = self.conn.prepare(
            "SELECT id, cron_expr, task, agent_type, profile_name, working_dir, project, task_group,
                    use_worktree, last_run_at, next_run_at, created_at, updated_at
             FROM scheduled_tasks
             ORDER BY next_run_at ASC, id ASC",
        )?;

        let rows = stmt.query_map([], map_scheduled_task)?;
        rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
    }

    pub fn list_due_scheduled_tasks(
        &self,
        now: chrono::DateTime<chrono::Utc>,
        limit: usize,

View on GitHub (pinned to 8321021c54)