bytedance/deer-flow · error · CheckpointModeMismatchError

Thread requires delta mode; materialize and convert its chec

Error message

Thread requires delta mode; materialize and convert its checkpoints before using full mode.

What it means

CheckpointModeMismatchError raised by the delta-gating checkpointer wrapper: the thread's checkpoints use the delta (incremental) format, but the accessor was constructed in full mode. Full-mode reads would silently serve partial state, so any aget/ahistory touching a delta checkpoint raises instead. Writes are not supported on this wrapper; mutation paths use the graph-backed accessor.

Source

Thrown at backend/app/gateway/services.py:771

class _RawCheckpointReadAccessor:
    """Degraded full-mode read accessor for when the agent factory is down.

    Full-mode checkpoints persist complete ``channel_values``, so reads do not
    need the compiled graph. The fail-closed delta gate still applies: delta
    checkpoints are rejected with :class:`CheckpointModeMismatchError` instead
    of being served as partial state. Writes are unsupported — mutation paths
    keep using the graph-backed accessor.
    """

    def __init__(self, checkpointer: Any, mode: str) -> None:
        self.checkpointer = checkpointer
        self.mode = mode

    @staticmethod
    def _gate(tup: Any) -> None:
        if checkpoint_tuple_uses_delta(tup):
            raise CheckpointModeMismatchError("Thread requires delta mode; materialize and convert its checkpoints before using full mode.")

    async def aget(self, config: dict[str, Any]) -> _RawCheckpointSnapshot:
        tup = await self.checkpointer.aget_tuple(config)
        self._gate(tup)
        return _RawCheckpointSnapshot(config, tup)

    async def ahistory(self, config: dict[str, Any], *, limit: int | None = None) -> list[_RawCheckpointSnapshot]:
        if limit is not None and limit <= 0:
            return []
        result: list[_RawCheckpointSnapshot] = []
        before = None
        walk_config = config
        if config.get("configurable", {}).get("checkpoint_id"):
            # Pregel's get_state_history treats config.checkpoint_id as the
            # inclusive start of the walk, while alist(before=...) is
            # exclusive — fetch the anchor explicitly so the degraded path
            # matches the graph path.
            before = config

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Materialize and convert the thread's checkpoints from delta to full format using DeerFlow's checkpoint conversion/migration tooling, then retry.
  2. Serve reads for that thread through delta mode (the accessor mode that matches how it was written).
  3. Avoid mixed versions writing/reading the same thread store; upgrade all gateway replicas together.
Defensive patterns

Strategy: fallback

Try / catch

catch CheckpointModeMismatchError; fall back to delta-mode reads for that thread, and schedule checkpoint conversion before retrying full mode.

Prevention

When it happens

Trigger: Reading state/history of a thread created or written by a DeerFlow version that stores delta checkpoints, through a code path that requests full-mode snapshots (e.g. a state or history endpoint configured for full mode).

Common situations: Upgrading/migrating between DeerFlow versions where the checkpoint format changed to delta; mixed-version deployments where one gateway writes deltas and another reads in full mode; running a migration halfway.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/725f3ce4aab831d1. Report an issue: GitHub.