{"record":{"id":"8d5a5c1a674fa85b","repo":"tinyhumansai/openhuman","slug":"flow-id-not-found","errorCode":null,"errorMessage":"flow '{id}' not found","messagePattern":"flow '(.+?)' not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/flows/store.rs","lineNumber":449,"sourceCode":"/// `ComposioTriggerReceived` event against every enabled `app_event` flow —\n/// scanning the (small) enabled set once per event is simpler and cheap\n/// enough at expected flow counts; a dedicated toolkit/trigger_slug index is\n/// a later optimization if this ever shows up as a bottleneck.\n///\n/// Returns `(flows, skipped)` — see [`list_flows`]. A corrupt row here must\n/// not take down `app_event` dispatch for every *other* enabled flow (R-M4).\npub fn list_enabled_flows(config: &Config) -> Result<(Vec<Flow>, usize)> {\n    with_connection(config, |conn| list_flow_rows(conn, \"WHERE enabled = 1\"))\n}\n\n/// Deletes a flow by id. Returns an error if no such flow exists.\npub fn remove_flow(config: &Config, id: &str) -> Result<()> {\n    let changed = with_connection(config, |conn| {\n        conn.execute(\"DELETE FROM flow_definitions WHERE id = ?1\", params![id])\n            .context(\"Failed to delete flow definition\")\n    })?;\n    if changed == 0 {\n        anyhow::bail!(\"flow '{id}' not found\");\n    }\n    tracing::debug!(flow_id = %id, \"[flows] removed flow definition\");\n    Ok(())\n}\n\n/// Toggles a flow's `enabled` flag, returning the updated record.\npub fn set_enabled(config: &Config, id: &str, enabled: bool) -> Result<Flow> {\n    let now = Utc::now().to_rfc3339();\n    let changed = with_connection(config, |conn| {\n        conn.execute(\n            \"UPDATE flow_definitions SET enabled = ?1, updated_at = ?2 WHERE id = ?3\",\n            params![if enabled { 1 } else { 0 }, now, id],\n        )\n        .context(\"Failed to update flow enabled state\")\n    })?;\n    if changed == 0 {\n        anyhow::bail!(\"flow '{id}' not found\");\n    }","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/flows/store.rs#L431-L467","documentation":"remove_flow's DELETE FROM flow_definitions affected zero rows, so no flow had that id and the delete bails instead of pretending success. Like its cron twin, the affected-row-count check distinguishes 'deleted' from 'nothing there'.","triggerScenarios":"flows.delete with a stale id (flow already removed from another session or tab); a typo'd id; a different workspace's flow store, which is per-workspace SQLite like cron.","commonSituations":"Double-submit of a delete button; retry after a timeout whose first attempt succeeded; scripts holding ids from an earlier listing; multiple open clients.","solutions":["List flows (flows.list) and confirm the id still exists; if gone, treat the delete as done","Re-copy the id from the current listing if the flow exists under a different id","Verify workspace/user profile if the listing itself is empty"],"exampleFix":"// before\nawait flowsApi.remove(flowId); // throws on already-deleted\n\n// after\ntry { await flowsApi.remove(flowId); }\ncatch (e) { if (!isNotFoundFlowError(e)) throw e; } // idempotent delete","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await removeFlow(id); } catch (e) { if (isNotFoundFlowError(e)) return ok('already deleted'); throw e; } — classify by the 'flow ... not found' message and treat it as success for idempotent deletes.","preventionTips":["Make UI delete actions idempotent (accept not-found as success)","Refresh the flow list after deletions from other tabs","Guard against double-submits by disabling buttons during the in-flight request"],"tags":["flows","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"}