{"record":{"id":"078ad5b0e3bc182e","repo":"excalidraw/excalidraw","slug":"invalid","errorCode":null,"errorMessage":"INVALID","messagePattern":"INVALID","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/excalidraw/data/image.ts","lineNumber":70,"sourceCode":"    try {\n      const encodedData = JSON.parse(metadata.text);\n      if (!(\"encoded\" in encodedData)) {\n        // legacy, un-encoded scene JSON\n        if (\n          \"type\" in encodedData &&\n          encodedData.type === EXPORT_DATA_TYPES.excalidraw\n        ) {\n          return metadata.text;\n        }\n        throw new Error(\"FAILED\");\n      }\n      return decode(encodedData);\n    } catch (error: any) {\n      console.error(error);\n      throw new Error(\"FAILED\");\n    }\n  }\n  throw new Error(\"INVALID\");\n};\n","sourceCodeStart":52,"sourceCodeEnd":72,"githubUrl":"https://github.com/excalidraw/excalidraw/blob/abeeaeba217ab3b5193b78c8d8d63c373b518ced/packages/excalidraw/data/image.ts#L52-L72","documentation":"Thrown by decodePngMetadata when the PNG has no usable Excalidraw tEXt chunk — either getTEXtChunk returned null (no tEXt chunk at all) or the tEXt chunk keyword is not MIME_TYPES.excalidraw. Unlike FAILED, INVALID is an expected outcome for ordinary PNGs and means 'no embedded scene here'.","triggerScenarios":"Passing a plain PNG with no tEXt chunk; passing a PNG whose tEXt chunk is keyed for a different application (e.g. 'Comment', 'Software'); or calling decodePngMetadata on a PNG exported with 'embed scene' disabled.","commonSituations":"Treating an arbitrary uploaded PNG as an Excalidraw scene, importing a PNG that was re-saved by an image editor that strips or renames metadata chunks, or a user opening a non-Excalidraw PNG via the scene-import path.","solutions":["Treat INVALID as 'no scene present' and fall back to importing the PNG as a plain image element rather than as a scene.","If a scene is expected, re-export the PNG from Excalidraw with 'Embed scene' / exportEmbedScene enabled so the tEXt chunk is written.","Verify the PNG was not passed through an optimizer (tinypng, imagemagick with strip) that drops ancillary chunks.","Pre-check with getTEXtChunk(blob) before calling decodePngMetadata to branch cleanly."],"exampleFix":"// before\nconst scene = await decodePngMetadata(pngBlob); // throws INVALID on plain PNG\n\n// after: probe first, fall back to image import\nimport { getTEXtChunk } from \"@excalidraw/excalidraw/data/image\";\nconst chunk = await getTEXtChunk(pngBlob);\nif (chunk?.keyword === MIME_TYPES.excalidraw) {\n  const scene = await decodePngMetadata(pngBlob);\n} else {\n  // insert as image element instead\n}","handlingStrategy":"validation","validationCode":"import { getTEXtChunk } from \"@excalidraw/excalidraw/data/image\";\nimport { MIME_TYPES } from \"@excalidraw/common\";\n\nconst chunk = await getTEXtChunk(blob);\nconst hasExcalidrawScene = chunk?.keyword === MIME_TYPES.excalidraw;\nif (!hasExcalidrawScene) {\n  // skip decodePngMetadata; import as image instead\n}","typeGuard":"const hasExcalidrawTEXtChunk = async (blob: Blob): Promise<boolean> => {\n  const chunk = await getTEXtChunk(blob);\n  return chunk?.keyword === MIME_TYPES.excalidraw;\n};","tryCatchPattern":"try { await decodePngMetadata(blob); }\ncatch (e) { if (e.message === \"INVALID\") { /* no scene — handle gracefully */ } }","preventionTips":["Use getTEXtChunk to branch before calling decodePngMetadata.","Expect INVALID for any non-Excalidraw PNG; do not treat it as a bug.","Disable PNG optimizers that strip tEXt chunks when embedding scenes."],"tags":["png-metadata","scene-decode","no-scene"],"analyzedSha":"abeeaeba217ab3b5193b78c8d8d63c373b518ced","analyzedAt":"2026-08-12T02:40:20.097Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}