paperclipai/paperclip · error

${body?.error ?? `Trace download failed: ${response.status}`

Error message

${body?.error ?? `Trace download failed: ${response.status}`}

What it means

The heartbeats API trace-download helper throws this Error when fetching a heartbeat trace file returns non-OK. It prefers the server's `error` field from the JSON body and otherwise reports `Trace download failed: <status>`. Tenant session recovery is attempted before throwing.

Source

Thrown at ui/src/api/heartbeats.ts:180

      {},
    ),
  deleteProviderTrace: (runId: string) =>
    api.delete<{ ok: true }>(`/heartbeat-runs/${runId}/provider-trace`),
  downloadProviderTrace: async (runId: string): Promise<Blob> => {
    const response = await fetch(
      `/api/heartbeat-runs/${runId}/provider-trace/download`,
      {
        credentials: "include",
        headers: { Accept: "application/x-ndjson" },
      },
    );
    if (!response.ok) {
      const body = (await response.json().catch(() => null)) as {
        error?: string;
      } | null;
      const recovery = tenantSessionRecovery.recoverIfNeeded(response.status, body);
      if (recovery) return recovery;
      throw new Error(
        body?.error ?? `Trace download failed: ${response.status}`,
      );
    }
    return response.blob();
  },
  workspaceOperationLog: (
    operationId: string,
    offset = 0,
    limitBytes = 256000,
  ) =>
    api.get<{
      operationId: string;
      store: string;
      logRef: string;
      content: string;
      nextOffset?: number;
    }>(
      `/workspace-operations/${operationId}/log?offset=${encodeURIComponent(String(offset))}&limitBytes=${encodeURIComponent(String(limitBytes))}`,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Confirm the heartbeat run still exists and its trace was persisted
  2. Check the download URL/id corresponds to the right company
  3. Check server storage/logs for trace write failures
  4. Retry transient 5xx with backoff

Example fix

// before
throw new Error(body?.error ?? `Trace download failed: ${response.status}`);
// after
if (response.status === 404) throw new Error('Trace artifact is no longer available.');
throw new Error(body?.error ?? `Trace download failed: ${response.status}`);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!heartbeat?.traceAvailable) throw new Error('No trace artifact for this run');

Type guard

function hasTraceError(b: unknown): b is { error: string } { return typeof b === 'object' && b !== null && typeof (b as any).error === 'string'; }

Try / catch

try { const blob = await heartbeatsApi.downloadTrace(id); } catch (e) { if (/Trace download failed: 404/.test(e.message)) showToast('Trace expired'); else if (/failed: 5/.test(e.message)) retryDownload(); else throw e; }

Prevention

When it happens

Trigger: Trace blob endpoint returns 404 (trace file pruned or never written), 403 (wrong company scope), or 500 (storage failure); response body is non-JSON so body is null.

Common situations: Old heartbeat whose trace artifacts were garbage-collected, trace larger than storage limits causing server 500, agent key lacking access to the company that owns the run.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/e10065a9ca617247. Report an issue: GitHub.