warpdotdev/warp · error
No sqlite sender available.
Error message
No sqlite sender available.
What it means
`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).
Source
Thrown at app/src/ai/blocklist/history_model.rs:1868
})
.collect();
if truncated_tasks.is_empty() {
return Err(anyhow!(
"Truncated tasks for forked conversation at block are empty for conversation {}.",
conversation.id()
));
}
let updated_tasks_with_new_ids =
update_forked_task_properties(truncated_tasks, prefix, false, title_override);
let Some(sqlite_sender) = GlobalResourceHandlesProvider::as_ref(app)
.get()
.model_event_sender
.clone()
else {
return Err(anyhow!("No sqlite sender available."));
};
// We preserve reverted action IDs. Orphaned IDs (for actions not in fork) are harmless.
// The reverted states are only copied to the new conversation if the revert happened before the user clicked fork,
// but regardless of when the revert happened relative to the fork point.
//
// Example:
// 1. Agent edit action
// 2. Agent edit action
// 3. User reverts edit from 1
// 4. **User clicks fork**
// 5. User reverts edit from 2
//
// In this example, the forked conversation will always show edit 1 as reverted and edit 2 as not reverted,
// regardless of if the fork point is between 2 and 3 or 3 and 4. This is because we preserve all prior reverts,
// either if they game before or after the fork point. However, once forked, we don't copy later reverts.
let reverted_action_ids = if conversation.reverted_action_ids().is_empty() {
NoneView on GitHub (pinned to e72fd7aacb)
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
Example fix
// before: discovered deep in the model
let Some(sqlite_sender) = GlobalResourceHandlesProvider::as_ref(app).get().model_event_sender.clone() else {
return Err(anyhow!("No sqlite sender available."));
};
// after: fail fast at the entry point so the UI never offers an impossible fork
if !GlobalResourceHandlesProvider::is_initialized(app) {
disable_fork_actions(ctx); // defer until startup completes
return;
} Defensive patterns
Strategy: validation
Validate before calling
// Before exposing fork actions, verify the global handles exist.
let globals_ready = GlobalResourceHandlesProvider::as_ref(app)
.get()
.model_event_sender
.is_some();
if !globals_ready {
disable_fork_menu(ctx); // defer until startup completes
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to persist forked conversation at block: {e:?}.
- Truncated tasks for forked conversation at block are empty f
AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16).
Data as JSON: /api/errors/341ee4ac0b45eb14.
Report an issue: GitHub.