{"record":{"id":"ca3e9778c85e0da4","repo":"withastro/astro","slug":"failed-to-parse-image-reference-imagepath","errorCode":null,"errorMessage":"Failed to parse image reference: ${imagePath}","messagePattern":"Failed to parse image reference: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/content/runtime.ts","lineNumber":503,"sourceCode":"\t\t\tlet image: GetImageResult;\n\t\t\tif (URL.canParse(decodedImagePath.src)) {\n\t\t\t\t// Remote image, pass through without resolving import\n\t\t\t\t// We know we should resolve this remote image because either:\n\t\t\t\t// 1. It was collected with the remark-collect-images plugin, which respects the astro image configuration,\n\t\t\t\t// 2. OR it was manually injected by another plugin, and we should respect that.\n\t\t\t\timage = await getImage(decodedImagePath);\n\t\t\t} else {\n\t\t\t\tconst id = imageSrcToImportId(decodedImagePath.src, fileName);\n\n\t\t\t\tconst imported = imageAssetMap.get(id);\n\t\t\t\tif (!id || imageObjects.has(id) || !imported) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\timage = await getImage({ ...decodedImagePath, src: imported });\n\t\t\t}\n\t\t\timageObjects.set(imagePath, image);\n\t\t} catch {\n\t\t\tthrow new Error(`Failed to parse image reference: ${imagePath}`);\n\t\t}\n\t}\n\n\treturn html.replaceAll(CONTENT_LAYER_IMAGE_REGEX, (full, imagePath) => {\n\t\tconst image = imageObjects.get(imagePath);\n\n\t\tif (!image) {\n\t\t\treturn full;\n\t\t}\n\n\t\tconst { index, ...attributes } = image.attributes;\n\n\t\treturn Object.entries({\n\t\t\t...attributes,\n\t\t\tsrc: image.src,\n\t\t\t// An empty `srcset` is invalid HTML, so only emit it when there are\n\t\t\t// actual candidates. This matches `vite-plugin-markdown/images.ts`.\n\t\t\t...(image.srcSet.values.length > 0 ? { srcset: image.srcSet.attribute } : {}),","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/content/runtime.ts#L485-L521","documentation":"When a markdown/MDX entry is rendered, Astro replaces the internal __ASTRO_IMAGE_ markers emitted by its remark image pipeline with optimized images. It first pre-loads every referenced image (JSON-decodes the marker, resolves a local path against the asset-imports map or treats it as a remote URL, then calls getImage). Any failure inside that block is rethrown as this generic error, hiding the original cause.","triggerScenarios":"An image whose serialized __ASTRO_IMAGE_ attribute fails JSON.parse (quotes or apostrophes in filenames serialized as entities); a relative image path absent from the astro:asset-imports map (file missing, moved, or outside the project); a remote image for which getImage throws (unreachable host, domain not allow-listed, invalid dimensions).","commonSituations":"Image filenames containing apostrophes or quotes (e.g. it's.png); deleting or renaming images still referenced by old content; remote images not configured under images.domains / images.remotePatterns; custom remark plugins injecting malformed image nodes; version upgrades that changed the marker encoding.","solutions":["Identify the failing entry from the stack/rendered file and inspect every image reference in that markdown.","Rename image files to remove quotes, apostrophes, and unusual characters, then update the references.","Verify each referenced local image exists on disk relative to the entry file.","For remote images, confirm the domain is allow-listed in the image config and reachable.","Temporarily disable custom remark/rehype plugins that rewrite image nodes to isolate the cause."],"exampleFix":"<!-- before: content/blog/post.md references an awkward filename -->\n![Cover](../../assets/it's a \"cover\".png)\n\n<!-- after: rename the asset and reference it plainly -->\n![Cover](../../assets/cover.png)","handlingStrategy":"try-catch","validationCode":"// Pre-build check: every local image referenced by markdown must exist\nimport { readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\n\nfor (const file of await collectMarkdown('src/content')) {\n  const text = await readFile(file, 'utf8');\n  for (const [, rel] of text.matchAll(/!\\[[^\\]]*\\]\\(([^)]+)\\)/g)) {\n    if (/^(https?:|data:)/.test(rel)) continue;\n    const target = resolve(dirname(file), rel.split(' ')[0]);\n    if (!existsSync(target)) console.error(`missing image ${rel} in ${file}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"// While looping a collection, isolate which entry has the bad reference\nfor (const entry of await getCollection('docs')) {\n  try {\n    await render(entry);\n  } catch (e) {\n    if (e instanceof Error && e.message.startsWith('Failed to parse image reference')) {\n      console.error(`bad image reference in ${entry.id}`);\n      continue;\n    }\n    throw e;\n  }\n}","preventionTips":["Keep image filenames free of quotes, apostrophes, and unusual entities.","Lint content for image references whose files do not exist as part of CI.","Keep custom remark plugins from rewriting image src values into non-string nodes.","Allow-list remote image domains in the image config before using them in content."],"tags":["images","markdown","content-collections","asset-resolution","remark"],"backgroundTag":"image-resolution-failed","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}