{"record":{"id":"61296e6f13116faf","repo":"moeru-ai/airi","slug":"failed-to-process-markdown-with-syntax-highlightin","errorCode":null,"errorMessage":"Failed to process markdown with syntax highlighting, using fallback:","messagePattern":"Failed to process markdown with syntax highlighting, using fallback:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-ui/src/components/markdown/markdown-renderer.vue","lineNumber":26,"sourceCode":"interface Props {\n  content: string\n  class?: string | string[]\n}\n\nconst props = defineProps<Props>()\n\nconst processedContent = ref('')\nconst { process, processSync } = useMarkdown()\nlet processRequestId = 0\n\nasync function processRichContent(content: string, requestId: number) {\n  try {\n    const result = DOMPurify.sanitize(await process(content))\n    if (requestId === processRequestId)\n      processedContent.value = result\n  }\n  catch (error) {\n    console.warn('Failed to process markdown with syntax highlighting, using fallback:', error)\n  }\n}\n\nfunction processContent() {\n  const content = props.content\n  const requestId = ++processRequestId\n\n  if (!content) {\n    processedContent.value = ''\n    return\n  }\n\n  try {\n    processedContent.value = DOMPurify.sanitize(processSync(content))\n  }\n  catch (error) {\n    console.warn('Failed to process markdown:', error)\n    processedContent.value = ''","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/components/markdown/markdown-renderer.vue#L8-L44","documentation":"The async markdown upgrade path (await process(content) followed by DOMPurify.sanitize) threw — most often inside the syntax-highlighting pipeline. The synchronous processSync render was already applied by processContent, and the catch leaves processedContent at that sync result, which is the 'fallback' the message refers to. Stale writes are prevented by the requestId === processRequestId check, so the un-highlighted render simply stays.","triggerScenarios":"Code fence with an unknown or misspelled language id; the highlighter bundle failing to load (offline dynamic import, HMR teardown); very large inputs failing in the async path.","commonSituations":"Chat messages containing code blocks with exotic languages; dev-server offline for the highlighter bundle; very long pasted documents.","solutions":["Inspect the logged error: unknown language ids and bundle load failures are the usual causes","Normalize or strip language ids on code fences before processing","Bundle the highlighter locally instead of dynamic imports that can fail offline","Keep the sync fallback explicit: on catch, assign DOMPurify.sanitize(processSync(content)) guarded by the requestId check"],"exampleFix":"// before\ncatch (error) {\n  console.warn('Failed to process markdown with syntax highlighting, using fallback:', error)\n}\n\n// after: make the promised fallback explicit (sync render stays, highlighted one retries next watch)\ncatch (error) {\n  console.warn('Failed to process markdown with syntax highlighting, using fallback:', error)\n  if (requestId === processRequestId)\n    processedContent.value = DOMPurify.sanitize(processSync(content))\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const result = DOMPurify.sanitize(await process(content))\n  if (requestId === processRequestId) processedContent.value = result\n}\ncatch (error) {\n  console.warn('Failed to process markdown with syntax highlighting, using fallback:', error)\n  // sync render (processSync) already filled processedContent; it stays as the fallback\n}","preventionTips":["Bundle the highlighter locally instead of dynamic CDN imports so HMR/offline cannot break it","Normalize code-fence language ids before highlighting","Keep the requestId check on every write so stale renders cannot win the race","Make the fallback explicit in the catch by re-assigning the sanitized sync result"],"tags":["markdown","shiki","syntax-highlighting","dompurify","fallback"],"backgroundTag":"shiki-highlight-failed","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}