bytedance/deer-flow · error · StreamReplayGapError

Unable to recover SSE history after ${recoveryAttempts} atte

Error message

Unable to recover SSE history after ${recoveryAttempts} attempts (requested ${gap.requested_event_id ?? "initial stream"}, earliest ${gap.earliest_available_event_id})

What it means

Raised when owner_user_id is empty after strip(). Trusted-mode removal made owner_user_id mandatory: every memory operation is scoped to an owning user, so the backend refuses to construct a config that would attribute memories to nobody.

Source

Thrown at frontend/src/core/api/api-client.ts:273

      if (entry.event === "gap") {
        gap = parseStreamReplayGap(entry.data);
        break;
      }
      yield entry;
    }

    if (!gap) {
      return;
    }

    const runId = expectedRunId() ?? gap.run_id;
    if (!threadId || gap.run_id !== runId) {
      throw new Error(
        "Stream replay gap does not match the active thread run.",
      );
    }
    if (recoveryAttempts >= MAX_STREAM_GAP_RECOVERIES) {
      throw new StreamReplayGapError(gap, recoveryAttempts);
    }
    recoveryAttempts += 1;

    // The SDK would otherwise ignore an unknown `gap` event and report a
    // normal finish. Surface a custom control event to DeerFlow's hook, reload
    // durable values, then explicitly follow only events newer than the
    // retained tail captured by the server.
    clearReconnectRun(threadId, runId);
    yield {
      event: "custom",
      data: { type: "stream_replay_gap", ...gap },
    };

    const durableState = await client.threads
      .getState(threadId)
      .catch((error: unknown) => {
        throw new StreamReplayGapError(gap, recoveryAttempts, error);
      });

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Set owner_user_id to the real owning user identifier, e.g. owner_user_id: user-42.
  2. If the value comes from templating, verify the variable expands and fail at template time when unset.
  3. Restart or reload the Gateway after fixing config.yaml.

Example fix

# before
backend_config:
  api_key_env: OPENVIKING_API_KEY

# after
backend_config:
  api_key_env: OPENVIKING_API_KEY
  owner_user_id: user-42
Defensive patterns

Strategy: validation

Validate before calling

def check_owner_user_id(value: str) -> None:
    assert value.strip(), 'owner_user_id must be set (trusted mode is gone)'

Prevention

When it happens

Trigger: Omitting owner_user_id from the openviking backend_config, or setting it to an empty or whitespace-only string (often via an unset template expansion). Detected during _validate at config load.

Common situations: Migrating from trusted-mode config that had no owner concept; per-user deployments where a template variable was supposed to inject the id but expanded empty; deleting the line while editing.

Related errors


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