tinyhumansai/openhuman · warning · anyhow::Error

Task source '{id}' not found.

Error message

Task source '{id}' not found.

What it means

Thrown by `task_sources::store::remove_source` when `DELETE FROM task_sources WHERE id = ?1` reports zero changed rows — the id was not present to begin with. It is a guard so callers can distinguish 'deleted' from 'nothing was there'. The `Failed to delete task source` context on the SQL error itself is a different, lower-level failure; this message means the DELETE executed fine but matched nothing.

Source

Thrown at src/openhuman/integrations/task_sources/store.rs:223

                i64::from(source.max_tasks_per_fetch),
                source.assigned_executor,
                id,
            ],
        )
        .context("Failed to update task source")?;
        Ok(())
    })?;

    get_source(config, id)
}

pub fn remove_source(config: &Config, id: &str) -> Result<()> {
    let changed = with_connection(config, |conn| {
        conn.execute("DELETE FROM task_sources WHERE id = ?1", params![id])
            .context("Failed to delete task source")
    })?;
    if changed == 0 {
        anyhow::bail!("Task source '{id}' not found");
    }
    Ok(())
}

/// Update a source's `last_fetch_at` / `last_status` after a fetch pass.
pub fn record_fetch(
    config: &Config,
    id: &str,
    finished_at: DateTime<Utc>,
    reason: FetchReason,
    status: &str,
) -> Result<()> {
    let line = format!("{}: {status}", reason.as_str());
    with_connection(config, |conn| {
        conn.execute(
            "UPDATE task_sources SET last_fetch_at = ?1, last_status = ?2 WHERE id = ?3",
            params![finished_at.to_rfc3339(), line, id],
        )

View on GitHub (pinned to 7491200858)

Solutions

  1. Treat 'not found' on delete as success if your operation is idempotent — catch and match the message.
  2. Refresh source ids via `list_sources` before showing/issuing delete actions.
  3. Guard double-submits in the UI (disable the delete button after first click).
  4. If the delete should always match, audit for another actor deleting concurrently.

Example fix

// before
store::remove_source(&config, &id)?;

// after — idempotent delete
match store::remove_source(&config, &id) {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("not found") => Ok(()), // already gone
    Err(e) => Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

let still_there = task_sources::store::list_sources(&config)?
    .iter().any(|s| s.id == id);
if !still_there { /* already deleted; nothing to do */ }

Try / catch

match task_sources::store::remove_source(&config, &id) {
    Ok(()) => Ok(()),
    Err(e) if e.to_string().contains("not found") => Ok(()), // idempotent delete
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: `remove_source(config, id)` with an already-deleted id, a double-submit of a delete RPC, or concurrent deletion (two actors racing to remove the same source).

Common situations: Idempotent-looking retry UIs that re-send the delete after a timeout when the first attempt actually succeeded; UI list state gone stale; cleanup loops that iterate ids collected before another process deleted them.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/eaae06a997f183a7. Report an issue: GitHub.