{"record":{"id":"a4cf6a3ac6275277","repo":"tinyhumansai/openhuman","slug":"cron-job-id-not-found","errorCode":null,"errorMessage":"Cron job '{id}' not found","messagePattern":"Cron job '(.+?)' not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/cron/store.rs","lineNumber":280,"sourceCode":"        )?;\n\n        let mut rows = stmt.query(params![job_id])?;\n        if let Some(row) = rows.next()? {\n            map_cron_job_row(row).map_err(Into::into)\n        } else {\n            anyhow::bail!(\"Cron job '{job_id}' not found\")\n        }\n    })\n}\n\npub fn remove_job(config: &Config, id: &str) -> Result<()> {\n    let changed = with_connection(config, |conn| {\n        conn.execute(\"DELETE FROM cron_jobs WHERE id = ?1\", params![id])\n            .context(\"Failed to delete cron job\")\n    })?;\n\n    if changed == 0 {\n        anyhow::bail!(\"Cron job '{id}' not found\");\n    }\n\n    println!(\"✅ Removed cron job {id}\");\n    Ok(())\n}\n\n/// Deletes every cron job in the workspace. Returns the number of rows removed.\n///\n/// Intended for the `openhuman.test_reset` RPC used by E2E specs to wipe state\n/// between tests without restarting the sidecar. The cron scheduler picks up\n/// the empty table on its next tick — no in-memory cache to invalidate.\npub fn clear_all_jobs(config: &Config) -> Result<usize> {\n    let removed = with_connection(config, |conn| {\n        conn.execute(\"DELETE FROM cron_jobs\", params![])\n            .context(\"Failed to clear cron jobs\")\n    })?;\n    log::info!(\"[cron] cleared all cron jobs (removed {removed} rows)\");\n    Ok(removed)","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/cron/store.rs#L262-L298","documentation":"remove_job executes DELETE FROM cron_jobs WHERE id = ?1 and checks the affected-row count; zero rows means no such job existed, so it bails instead of reporting success. This makes delete outcomes explicit: callers can distinguish 'removed' from 'nothing there'.","triggerScenarios":"Deleting an already-deleted job — double-click, retried request, re-run script; a wrong or stale id; a different workspace's cron.db.","commonSituations":"UI retry after a timeout where the first DELETE actually succeeded; idempotent cleanup scripts re-run; concurrent sessions deleting the same job.","solutions":["Confirm via cron list whether the job still exists; if it is already gone, treat the outcome as success","Re-copy the id from a fresh listing if the job exists under a different id","Check workspace/user profile if the listing itself is empty"],"exampleFix":"// before\nremove_job(&config, id)?; // hard error on double-delete\n\n// after\nmatch remove_job(&config, id) {\n    Ok(()) => println!(\"removed\"),\n    Err(e) if e.to_string().contains(\"not found\") => println!(\"already gone\"),\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"For idempotent deletes, swallow the not-found branch and report success: match remove_job(...); on Err(e) whose message contains 'not found', return Ok — propagate every other error.","preventionTips":["Make delete operations idempotent at the caller by treating not-found as success","Debounce/duplicate-guard delete buttons in UIs","Log delete outcomes so silent double-deletes remain visible"],"tags":["cron","store","not-found","delete"],"backgroundTag":"record-not-found","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}