{"record":{"id":"341ee4ac0b45eb14","repo":"warpdotdev/warp","slug":"no-sqlite-sender-available","errorCode":null,"errorMessage":"No sqlite sender available.","messagePattern":"No sqlite sender available\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/ai/blocklist/history_model.rs","lineNumber":1868,"sourceCode":"            })\n            .collect();\n\n        if truncated_tasks.is_empty() {\n            return Err(anyhow!(\n                \"Truncated tasks for forked conversation at block are empty for conversation {}.\",\n                conversation.id()\n            ));\n        }\n\n        let updated_tasks_with_new_ids =\n            update_forked_task_properties(truncated_tasks, prefix, false, title_override);\n\n        let Some(sqlite_sender) = GlobalResourceHandlesProvider::as_ref(app)\n            .get()\n            .model_event_sender\n            .clone()\n        else {\n            return Err(anyhow!(\"No sqlite sender available.\"));\n        };\n\n        // We preserve reverted action IDs. Orphaned IDs (for actions not in fork) are harmless.\n        // The reverted states are only copied to the new conversation if the revert happened before the user clicked fork,\n        // but regardless of when the revert happened relative to the fork point.\n        //\n        // Example:\n        // 1. Agent edit action\n        // 2. Agent edit action\n        // 3. User reverts edit from 1\n        // 4. **User clicks fork**\n        // 5. User reverts edit from 2\n        //\n        // In this example, the forked conversation will always show edit 1 as reverted and edit 2 as not reverted,\n        // regardless of if the fork point is between 2 and 3 or 3 and 4. This is because we preserve all prior reverts,\n        // either if they game before or after the fork point. However, once forked, we don't copy later reverts.\n        let reverted_action_ids = if conversation.reverted_action_ids().is_empty() {\n            None","sourceCodeStart":1850,"sourceCodeEnd":1886,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/app/src/ai/blocklist/history_model.rs#L1850-L1886","documentation":"`fork_conversation_at_block` needs the global `model_event_sender` (obtained via GlobalResourceHandlesProvider) to persist the fork through the model-event pipeline. If the provider yields no sender, persistence is impossible and the fork aborts with this error. The sender is part of global singleton state wired during app startup, so its absence means the globals were never initialized, were torn down, or the context never had them (e.g. tests).","triggerScenarios":"Fork-at-block executes before GlobalResourceHandlesProvider has been initialized at startup, after it was cleared during shutdown, or in a headless/test context where the global resource handles were never installed.","commonSituations":"User triggers fork during a startup/shutdown race; unit tests driving history_model without installing the global provider; early-return bugs in app init leaving globals missing.","solutions":["Ensure GlobalResourceHandlesProvider is initialized before any conversation UI (including fork affordances) becomes reachable","Disable or defer fork actions when the app is quitting or globals are not yet ready","In tests, install a provider with a live model_event_sender before exercising fork flows"],"exampleFix":"// before: discovered deep in the model\nlet Some(sqlite_sender) = GlobalResourceHandlesProvider::as_ref(app).get().model_event_sender.clone() else {\n    return Err(anyhow!(\"No sqlite sender available.\"));\n};\n\n// after: fail fast at the entry point so the UI never offers an impossible fork\nif !GlobalResourceHandlesProvider::is_initialized(app) {\n    disable_fork_actions(ctx); // defer until startup completes\n    return;\n}","handlingStrategy":"validation","validationCode":"// Before exposing fork actions, verify the global handles exist.\nlet globals_ready = GlobalResourceHandlesProvider::as_ref(app)\n    .get()\n    .model_event_sender\n    .is_some();\nif !globals_ready {\n    disable_fork_menu(ctx); // defer until startup completes\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Initialize global resource handles before constructing conversation views","Block mutation actions (fork, revert, delete) during app shutdown","Add a startup smoke test that forks a conversation to catch missing globals early"],"tags":["initialization","persistence","global-state","fork"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}