Significant-Gravitas/AutoGPT · error · DownloadError

Download failed: ${res.status}

Error message

Download failed: ${res.status}

What it means

HTTP 400 from POST /onboarding/step when the submitted step value is not one of the Literal FrontendOnboardingStep values. The endpoint re-validates the step against get_args(FrontendOnboardingStep) even though the request body is typed, because a mismatched payload can bypass static typing at runtime.

Source

Thrown at autogpt_platform/frontend/src/app/(platform)/copilot/components/ArtifactPanel/downloadArtifact.ts:18

import type { ArtifactRef } from "../../store";

const MAX_RETRIES = 2;
const RETRY_DELAY_MS = 500;

function isTransientError(status: number): boolean {
  return status >= 500 || status === 408 || status === 429;
}

class DownloadError extends Error {}

async function fetchWithRetry(url: string, retries: number): Promise<Response> {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      const res = await fetch(url);
      if (res.ok) return res;
      if (!isTransientError(res.status) || attempt === retries) {
        throw new DownloadError(`Download failed: ${res.status}`);
      }
    } catch (error) {
      if (error instanceof DownloadError) throw error;
      if (attempt === retries) throw error;
    }
    await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
  }
  throw new Error("Unreachable");
}

/**
 * Trigger a file download from an artifact URL.
 *
 * Uses fetch+blob instead of a bare `<a download>` because the browser
 * ignores the `download` attribute on cross-origin responses (GCS signed
 * URLs), and some browsers require the anchor to be attached to the DOM
 * before `.click()` fires the download.
 *

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Send only step values taken from the API's current FrontendOnboardingStep literal set (check the OpenAPI schema for the enum).
  2. After upgrading the platform, regenerate frontend API types so the step union stays in sync.
  3. Validate the step client-side against the enum before posting.

Example fix

// before
await api.post('/onboarding/step', { step: 'welcome-tour' });

// after
const STEPS = ['profile-setup', 'agent-template', 'first-agent'] as const; // from generated types
const step = 'profile-setup' satisfies typeof STEPS[number];
await api.post('/onboarding/step', { step });
Defensive patterns

Strategy: type-guard

Validate before calling

const VALID_STEPS = getArgsFromOpenApiSchema(); // values of FrontendOnboardingStep
if (!VALID_STEPS.includes(step)) throw new Error(`Invalid step: ${step}`);
await api.post('/onboarding/step', { step });

Type guard

const isOnboardingStep = (s: string): s is FrontendOnboardingStep =>
  ['profile-setup', 'agent-template'].includes(s); // keep in sync with generated types

Try / catch

const resp = await api.post('/onboarding/step', { step });
if (resp.status === 400) { /* resync step enum from /openapi.json */ }

Prevention

When it happens

Trigger: Sending {"step": "unknown-step"} or any string outside the Literal set (e.g. an old step name after the enum changed, a typo, or a client built against a newer/older API version).

Common situations: Frontend/backend version skew after onboarding steps were added or renamed; hand-crafted API calls; form submitting before the step constant is set.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/d7fd863eba08c54b. Report an issue: GitHub.