bytedance/deer-flow · warning

detailMessage ?? `${fallbackMessage}: ${response.statusText}

Error message

detailMessage ?? `${fallbackMessage}: ${response.statusText}`

What it means

Thrown from readMemoryResponse when a /api/memory call returns non-2xx: it prefers the backend 'detail' message, falling back to '<fallbackMessage>: <statusText>'. The memory API persists per-user long-term memory, so failures typically reflect auth (401 after session expiry), disabled memory feature (404), or persistence backend errors (500).

Source

Thrown at frontend/src/core/memory/api.ts:75

      typeof detail === "boolean" ||
      typeof detail === "bigint"
    ) {
      return String(detail);
    }

    if (typeof detail === "symbol") {
      return detail.description ?? null;
    }

    return null;
  }

  if (!response.ok) {
    const errorData = (await response.json().catch(() => ({}))) as {
      detail?: unknown;
    };
    const detailMessage = formatErrorDetail(errorData.detail);
    throw new Error(
      detailMessage ?? `${fallbackMessage}: ${response.statusText}`,
    );
  }

  return response.json() as Promise<UserMemory>;
}

export async function loadMemory(): Promise<UserMemory> {
  const response = await fetch(`${getBackendBaseURL()}/api/memory`);
  return readMemoryResponse(response, "Failed to fetch memory");
}

export async function clearMemory(): Promise<UserMemory> {
  const response = await fetch(`${getBackendBaseURL()}/api/memory`, {
    method: "DELETE",
  });
  return readMemoryResponse(response, "Failed to clear memory");
}

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. On 401, re-authenticate (re-run the auth flow) and retry the memory call
  2. Check config.yaml enables the memory feature and the Gateway logged no startup warning
  3. Apply pending backend migrations so /api/memory has its table
  4. Read the thrown message — if it's the fallback text, inspect the raw response body server-side since no detail was returned

Example fix

// before
const mem = await loadMemory();

// after
const mem = await loadMemory().catch(async (e) => {
  if (String(e.message).startsWith('Failed to fetch memory')) {
    await reauthenticate();
    return loadMemory(); // one retry after fresh session
  }
  throw e;
});
Defensive patterns

Strategy: try-catch

Type guard

export function isMemoryFetchError(e: unknown): e is Error {
  return e instanceof Error && /^Failed to (fetch|save) memory/.test(e.message);
}

Try / catch

try {
  return await loadMemory();
} catch (e) {
  if (isMemoryFetchError(e) && await sessionExpired()) {
    await reauthenticate();
    return loadMemory(); // single retry
  }
  return EMPTY_USER_MEMORY; // memory is auxiliary
}

Prevention

When it happens

Trigger: loadMemory()/save after the auth session expired (401 → JSON detail or empty body → fallback text); memory feature flag off in config.yaml (404); database migration not applied so the memory table is missing (500).

Common situations: Long-lived tab where the token expired; upgrading the backend and forgetting `make migrate-rev`/startup migrations; SQLite file permissions lost in a Docker volume.

Related errors


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