linshenkx/prompt-optimizer · error · Error

Not a PNG file

Error message

Not a PNG file

What it means

Thrown by insertPngTextChunk when the input byte array fails the PNG signature check (isPng). The utility embeds a tEXt chunk into a PNG, so it requires a valid PNG file starting with the 8-byte PNG magic signature. Any non-PNG input (JPEG, WebP, corrupted bytes, or a data URL prefix that wasn't stripped) triggers this error.

Source

Thrown at packages/ui/src/utils/favorite-share-export.ts:1286

const crc32 = (bytes: Uint8Array): number => {
  const table = getCrcTable()
  let crc = 0xffffffff
  for (const byte of bytes) {
    crc = table[(crc ^ byte) & 0xff] ^ (crc >>> 8)
  }
  return (crc ^ 0xffffffff) >>> 0
}

const isPng = (bytes: Uint8Array): boolean =>
  PNG_SIGNATURE.every((byte, index) => bytes[index] === byte)

export const insertPngTextChunk = (
  pngBytes: Uint8Array,
  keyword: string,
  text: string,
): Uint8Array => {
  if (!isPng(pngBytes)) throw new Error('Not a PNG file')
  const encoder = new TextEncoder()
  const type = encoder.encode('tEXt')
  const data = encoder.encode(`${keyword}\u0000${text}`)
  const chunk = createPngChunk(type, data)

  return insertPngChunkAfterIhdr(pngBytes, chunk)
}

export const insertPngInternationalTextChunk = (
  pngBytes: Uint8Array,
  keyword: string,
  text: string,
  options: {
    compressed?: boolean
    languageTag?: string
    translatedKeyword?: string
  } = {},
): Uint8Array => {

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. Verify the input is a PNG before calling: check the first 8 bytes equal [137,80,78,71,13,10,26,10]
  2. If the image may be another format, re-encode it via canvas: draw to a canvas and call toBlob(cb, 'image/png')
  3. Ensure you decoded base64/data URL to Uint8Array before passing (strip the data:image/...;base64, prefix)
  4. If the PNG came from an export pipeline, confirm no compression/transcoding step (e.g. optimizer) altered the file

Example fix

// before
const bytes = base64ToBytes(dataUrl) // dataUrl was 'data:image/jpeg;base64,...'
const out = insertPngTextChunk(bytes, 'key', 'value')

// after
const pngDataUrl = await reEncodeAsPng(dataUrl)
const bytes = base64ToBytes(pngDataUrl.split(',')[1])
if (!isPng(bytes)) throw new Error('Expected PNG')
const out = insertPngTextChunk(bytes, 'key', 'value')
Defensive patterns

Strategy: type-guard

Validate before calling

const PNG_SIG = [137, 80, 78, 71, 13, 10, 26, 10]
const isPngBytes = (b: Uint8Array) =>
  b.length >= 8 && PNG_SIG.every((v, i) => b[i] === v)

if (!isPngBytes(bytes)) bytes = await reEncodeAsPng(bytes)

Type guard

const isPngBytes = (b: Uint8Array): b is Uint8Array & { __png: true } =>
  b.length >= 8 && [137,80,78,71,13,10,26,10].every((v,i) => b[i] === v)

Try / catch

try {
  const out = insertPngTextChunk(bytes, keyword, text)
} catch (e) {
  if (e instanceof Error && e.message === 'Not a PNG file') {
    // convert source to PNG and retry, or notify user
  } else throw e
}

Prevention

When it happens

Trigger: Calling insertPngTextChunk(pngBytes, keyword, text) with bytes that are not a PNG: a JPEG/WebP/GIF export, a truncated or modified byte array, or a canvas export that was requested as image/jpeg instead of image/png.

Common situations: Exporting images with toDataURL('image/jpeg') then passing decoded bytes to a PNG-embedding API; passing a screenshot re-encoded by the OS clipboard; passing base64 string directly instead of decoded Uint8Array; file picked with accept='image/*' instead of 'image/png'.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/0aa3fe8e822afa51. Report an issue: GitHub.