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
- Check for concurrent deletions of scheduled tasks between insert and readback.
- Ensure all statements on the connection use the same thread/serialized access so last_insert_rowid is valid.
- Wrap insert + readback in a transaction to make the invariant atomic.
- 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
- Always wrap INSERT + readback in a single transaction.
- Never share one SQLite connection across threads without serialization.
- Avoid relying on last_insert_rowid after conditional statements; capture the rowid immediately after the INSERT.
- Audit for background jobs that delete scheduled tasks aggressively.
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
- candidate alias integrity verification failed
- Remote dispatch request
- candidate alias collision with physical candidate id
- error
- a current bound approved draft is required
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)