warpdotdev/warp · error

hydrate_remote_child_placeholder_with_cloud_transcript: loca

Error message

hydrate_remote_child_placeholder_with_cloud_transcript: local placeholder {local_placeholder_id} not found in conversations_by_id; refusing to construct a detached merged conversation

What it means

`hydrate_remote_child_placeholder_with_cloud_transcript` merges a cloud-synced child-conversation transcript into a local placeholder conversation. It requires the placeholder (keyed by `local_placeholder_id`) to already exist in `conversations_by_id`; when missing, it deliberately errors instead of constructing a merged conversation that would be detached from the UI's existing conversation state. The merged result reuses the placeholder id via `AIConversation::new_restored`.

Source

Thrown at app/src/ai/blocklist/history_model.rs:2845

    /// (parent ids, agent_name, run_id, is_remote_child, pinned)
    /// authoritative. Cloud supplies the transcript and server-side metadata.
    ///
    /// Narrowly scoped to the remote-child placeholder hydration path
    /// (`pane_group::hydrate_remote_child_transcript_in_place`). Returns
    /// `Err` when the placeholder isn't loaded so the caller can fall back
    /// instead of silently producing a detached conversation.
    pub fn hydrate_remote_child_placeholder_with_cloud_transcript(
        &mut self,
        local_placeholder_id: AIConversationId,
        tasks: Vec<warp_multi_agent_api::Task>,
        cloud_conversation: AIConversation,
    ) -> anyhow::Result<AIConversation> {
        let placeholder = self
            .conversations_by_id
            .get(&local_placeholder_id)
            .cloned()
            .ok_or_else(|| {
                anyhow!(
                    "hydrate_remote_child_placeholder_with_cloud_transcript: \
                     local placeholder {local_placeholder_id} not found in conversations_by_id; \
                     refusing to construct a detached merged conversation"
                )
            })?;

        let merged_conversation_data =
            merged_remote_child_placeholder_conversation_data(&placeholder, &cloud_conversation);

        let mut merged = AIConversation::new_restored(
            local_placeholder_id,
            tasks,
            Some(merged_conversation_data),
        )?;
        merged.reassign_exchange_ids();

        if let Some(metadata) = cloud_conversation.server_metadata() {
            merged.set_server_metadata(metadata.clone());

View on GitHub (pinned to e72fd7aacb)

Solutions

  1. Create and insert the local placeholder conversation (into conversations_by_id) before dispatching hydration
  2. Order sync handling so placeholder creation is guaranteed to precede transcript application for the same id
  3. If the placeholder was intentionally deleted, drop the late transcript event instead of erroring
  4. On restart, re-fetch conversation metadata to rebuild placeholders before transcripts arrive

Example fix

// before
let merged = model.hydrate_remote_child_placeholder_with_cloud_transcript(id.clone(), tasks, cloud)?;

// after: ensure the placeholder exists, then hydrate
if model.conversation(&id).is_none() {
    model.insert_placeholder_conversation(id.clone(), cloud.metadata.clone());
}
let merged = model.hydrate_remote_child_placeholder_with_cloud_transcript(id, tasks, cloud)?;
Defensive patterns

Strategy: validation

Validate before calling

// Before hydrating, confirm the placeholder exists; recreate it from cloud metadata if not.
if model.conversation(&local_placeholder_id).is_none() {
    model.insert_placeholder_from(&cloud_conversation);
}

Type guard

fn is_hydratable(model: &BlocklistAIHistoryModel, id: &AIConversationId) -> bool {
    model.conversation(id).is_some()
}

Try / catch

match model.hydrate_remote_child_placeholder_with_cloud_transcript(id, tasks, cloud) {
    Ok(conv) => conv,
    Err(e) if e.to_string().contains("not found in conversations_by_id") => {
        model.insert_placeholder_from(&cloud);
        model.hydrate_remote_child_placeholder_with_cloud_transcript(id, tasks, cloud)?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A cloud transcript (tasks + cloud_conversation) is applied for a placeholder id the model does not know: the placeholder was never created locally, was deleted before the transcript event landed, the app restarted and cleared in-memory state before hydration, or sync events arrived out of order (transcript before placeholder creation).

Common situations: Multi-agent cloud conversations syncing into a freshly restarted client; user deleted the child conversation locally while an in-flight transcript event was still queued; id mismatch between the sync event and the locally stored placeholder.

Related errors


AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16). Data as JSON: /api/errors/b2e7f6042f17aad2. Report an issue: GitHub.