{"record":{"id":"d7fd863eba08c54b","repo":"Significant-Gravitas/AutoGPT","slug":"download-failed-res-status-d7fd86","errorCode":null,"errorMessage":"Download failed: ${res.status}","messagePattern":"Download failed: (.+?)","errorType":"exception","errorClass":"DownloadError","httpStatus":null,"severity":"error","filePath":"autogpt_platform/frontend/src/app/(platform)/copilot/components/ArtifactPanel/downloadArtifact.ts","lineNumber":18,"sourceCode":"import type { ArtifactRef } from \"../../store\";\n\nconst MAX_RETRIES = 2;\nconst RETRY_DELAY_MS = 500;\n\nfunction isTransientError(status: number): boolean {\n  return status >= 500 || status === 408 || status === 429;\n}\n\nclass DownloadError extends Error {}\n\nasync function fetchWithRetry(url: string, retries: number): Promise<Response> {\n  for (let attempt = 0; attempt <= retries; attempt++) {\n    try {\n      const res = await fetch(url);\n      if (res.ok) return res;\n      if (!isTransientError(res.status) || attempt === retries) {\n        throw new DownloadError(`Download failed: ${res.status}`);\n      }\n    } catch (error) {\n      if (error instanceof DownloadError) throw error;\n      if (attempt === retries) throw error;\n    }\n    await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));\n  }\n  throw new Error(\"Unreachable\");\n}\n\n/**\n * Trigger a file download from an artifact URL.\n *\n * Uses fetch+blob instead of a bare `<a download>` because the browser\n * ignores the `download` attribute on cross-origin responses (GCS signed\n * URLs), and some browsers require the anchor to be attached to the DOM\n * before `.click()` fires the download.\n *","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/frontend/src/app/(platform)/copilot/components/ArtifactPanel/downloadArtifact.ts#L1-L36","documentation":"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.","triggerScenarios":"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).","commonSituations":"Frontend/backend version skew after onboarding steps were added or renamed; hand-crafted API calls; form submitting before the step constant is set.","solutions":["Send only step values taken from the API's current FrontendOnboardingStep literal set (check the OpenAPI schema for the enum).","After upgrading the platform, regenerate frontend API types so the step union stays in sync.","Validate the step client-side against the enum before posting."],"exampleFix":"// before\nawait api.post('/onboarding/step', { step: 'welcome-tour' });\n\n// after\nconst STEPS = ['profile-setup', 'agent-template', 'first-agent'] as const; // from generated types\nconst step = 'profile-setup' satisfies typeof STEPS[number];\nawait api.post('/onboarding/step', { step });","handlingStrategy":"type-guard","validationCode":"const VALID_STEPS = getArgsFromOpenApiSchema(); // values of FrontendOnboardingStep\nif (!VALID_STEPS.includes(step)) throw new Error(`Invalid step: ${step}`);\nawait api.post('/onboarding/step', { step });","typeGuard":"const isOnboardingStep = (s: string): s is FrontendOnboardingStep =>\n  ['profile-setup', 'agent-template'].includes(s); // keep in sync with generated types","tryCatchPattern":"const resp = await api.post('/onboarding/step', { step });\nif (resp.status === 400) { /* resync step enum from /openapi.json */ }","preventionTips":["Regenerate frontend types after backend upgrades","Drive step values from the OpenAPI schema, not hardcoded strings"],"tags":["onboarding","validation","api-contract"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}