BloopAI/vibe-kanban · error · Error

Export failed (${response.status})

Error message

Export failed (${response.status})

What it means

ExportDownload POSTs an export request and streams the resulting zip. If the export endpoint responds with a non-ok status it throws `Export failed (${response.status})`, embedding the HTTP status code. Filename is then parsed from content-disposition with a default of 'vibe-kanban-export.zip'.

Source

Thrown at packages/web-core/src/features/export/ui/ExportDownload.tsx:49

  const [error, setError] = useState<string | null>(null);
  const [downloadUrl, setDownloadUrl] = useState<string | null>(null);
  const [filename, setFilename] = useState('vibe-kanban-export.zip');
  const hasStartedRef = useRef(false);

  const startExport = useCallback(async () => {
    setIsExporting(true);
    setError(null);
    setDownloadUrl(null);

    try {
      const response = await exportFn({
        organization_id: orgId,
        project_ids: projectIds,
        include_attachments: includeAttachments,
      });

      if (!response.ok) {
        throw new Error(`Export failed (${response.status})`);
      }

      let downloadFilename = 'vibe-kanban-export.zip';
      const disposition = response.headers.get('content-disposition');
      if (disposition) {
        const match = disposition.match(/filename="?([^"]+)"?/);
        if (match) {
          downloadFilename = match[1];
        }
      }
      setFilename(downloadFilename);

      const blob = await response.blob();
      const url = URL.createObjectURL(blob);
      setDownloadUrl(url);

      const a = document.createElement('a');
      a.href = url;

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Read the status in the message: 401 → re-authenticate; 403 → check org permissions; 500/504 → retry with fewer projects or attachments off
  2. Reduce export scope (fewer project_ids, includeAttachments=false) and retry
  3. Verify orgId/projectIds are current values, not stale route state
  4. Implement a polling/async export flow for very large exports instead of a single request

Example fix

// before
await runExport({ orgId, projectIds, includeAttachments: true });
// after
try {
  await runExport({ orgId, projectIds, includeAttachments: true });
} catch (e) {
  if (String(e).includes('504')) await runExport({ orgId, projectIds: projectIds.slice(0, 10), includeAttachments: false });
  else notify.error(String(e));
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!orgId) throw new Error('organization required');
if (!projectIds.length || projectIds.length > 100) throw new Error('select 1–100 projects to export');

Type guard

function isExportOptions(o: unknown): o is ExportOptions { return typeof o === 'object' && o !== null && typeof (o as any).orgId === 'string' && Array.isArray((o as any).projectIds); }

Try / catch

try {
  await runExport({ orgId, projectIds, includeAttachments });
} catch (e) {
  const m = /Export failed \((\d+)\)/.exec(String(e));
  const status = m ? Number(m[1]) : 0;
  if (status === 401) reauth(); else notify.error(String(e));
}

Prevention

When it happens

Trigger: Export request returns non-ok: too many/too large project_ids (payload limit), server timeout building a huge export, 401/403 on the org scope, or export service unavailable.

Common situations: Exporting an entire large organization times out (504); include_attachments balloons payload size; session expires mid-export (401); misconfigured orgId from stale route params.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/76682bfb25c5748c. Report an issue: GitHub.