{"record":{"id":"d2964112e9f54fcf","repo":"unionlabs/union","slug":"failed-to-download-asset-asset-id-response-s","errorCode":null,"errorMessage":"Failed to download asset ${asset.id}: ${response.status} ${response.statusText}","messagePattern":"Failed to download asset (.+?): (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"site/scripts/import-contentful-export.mjs","lineNumber":171,"sourceCode":"const downloadableAssets = assetManifest.filter(\n  (asset) => asset.sourceUrl && asset.path,\n)\n\nawait mapConcurrent(downloadableAssets, 8, async (asset) => {\n  const target = join(SITE_DIRECTORY, \"public\", asset.path)\n  await mkdir(join(target, \"..\"), { recursive: true })\n\n  let existingSize = null\n  try {\n    existingSize = (await stat(target)).size\n  } catch {\n    // The file has not been downloaded yet.\n  }\n\n  if (existingSize !== asset.size) {\n    const response = await fetch(asset.sourceUrl)\n    if (!response.ok) {\n      throw new Error(\n        `Failed to download asset ${asset.id}: ${response.status} ${response.statusText}`,\n      )\n    }\n    const bytes = new Uint8Array(await response.arrayBuffer())\n    if (asset.size !== null && bytes.byteLength !== asset.size) {\n      throw new Error(\n        `Asset ${asset.id} size mismatch: expected ${asset.size}, received ${bytes.byteLength}`,\n      )\n    }\n    await writeFile(target, bytes)\n  }\n\n  completedAssets += 1\n  if (\n    completedAssets % 25 === 0\n    || completedAssets === downloadableAssets.length\n  ) {\n    console.log(`assets: ${completedAssets}/${downloadableAssets.length}`)","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/unionlabs/union/blob/031785bb6dc6b957c624e62bc64c184409c97d7b/site/scripts/import-contentful-export.mjs#L153-L189","documentation":"Thrown while the Contentful import script downloads asset binaries: for each asset whose local file size differs from the export's recorded size, it fetches asset.sourceUrl; a non-ok HTTP response aborts the import with the asset id plus status code and reason.","triggerScenarios":"Contentful CDN returning 404 (asset deleted or URL changed since the export), 401/403 (space made private, signed URLs expired), or 5xx (CDM outage) when GET-ing assets.contentful.com/<space>/<asset>/<name>.","commonSituations":"Old export archives whose asset URLs no longer resolve; spaces switched from public to private; network/proxy blocking the CDN; running the import long after the export was taken.","solutions":["Re-export the space from Contentful so sourceUrl values are current, then rerun the import (already-downloaded assets are skipped by the size check)","Check the specific asset id in the message in the Contentful web app; if it was deleted, remove it from the export or re-publish","Verify network access to assets.contentful.com (proxy/firewall) and retry — the size-mismatch skip makes reruns cheap"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import { stat } from \"node:fs/promises\"\n\nconst needsDownload = async (target: string, expectedSize: number | null) => {\n  try {\n    return (await stat(target)).size !== expectedSize\n  } catch {\n    return true\n  }\n}\n// The script already skips matching files; pre-check connectivity if you must:\nawait fetch(asset.sourceUrl, { method: \"HEAD\" })","typeGuard":null,"tryCatchPattern":"for (const asset of assets) {\n  try {\n    await downloadAsset(asset)\n  } catch (error) {\n    if ((error as Error).message.startsWith(`Failed to download asset ${asset.id}`)) {\n      // log and continue; rerun later — completed assets are skipped via the size check\n      continue\n    }\n    throw error\n  }\n}","preventionTips":["Run the import soon after taking the Contentful export so asset URLs are fresh","Rerun on failure — the size-equality check makes downloads idempotent","Delete stale assets from the export if they no longer exist in the space"],"tags":["contentful","assets","download","scripts","network"],"backgroundTag":null,"analyzedSha":"031785bb6dc6b957c624e62bc64c184409c97d7b","analyzedAt":"2026-08-16T06:24:09.996Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}