stablyai/orca · warning · Error

Repo icons must be PNG files.

Error message

Repo icons must be PNG files.

What it means

Thrown by the shell:pickRepoIcon handler when the chosen file's extension is not in REPO_ICON_IMAGE_MIME_TYPES, which currently contains only '.png' -> 'image/png'. The dialog filter suggests PNG, but the actual file is double-checked by extension to its lowercase MIME; a jpg, gif, webp, or any non-png file is rejected.

Source

Thrown at src/main/ipc/shell.ts:269

    return result.filePaths[0]
  })

  ipcMain.handle(
    'shell:pickRepoIconImage',
    async (): Promise<{ dataUrl: string; fileName: string } | null> => {
      const result = await dialog.showOpenDialog({
        properties: ['openFile'],
        filters: [{ name: 'Repo icon images', extensions: ['png'] }]
      })
      if (result.canceled || result.filePaths.length === 0) {
        return null
      }

      const filePath = result.filePaths[0]
      const extension = extname(filePath).toLowerCase()
      const mimeType = REPO_ICON_IMAGE_MIME_TYPES[extension]
      if (!mimeType) {
        throw new Error('Repo icons must be PNG files.')
      }

      const stats = await stat(filePath)
      if (stats.size > MAX_REPO_ICON_UPLOAD_BYTES) {
        throw new Error('Repo icon image must be 256KB or smaller.')
      }

      const buffer = await readFile(filePath)
      return {
        dataUrl: `data:${mimeType};base64,${buffer.toString('base64')}`,
        fileName: basename(filePath)
      }
    }
  )

  ipcMain.handle('shell:pickAudio', async (): Promise<string | null> => {
    const result = await dialog.showOpenDialog({
      properties: ['openFile'],

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Convert the image to PNG before selecting it (any image editor or 'magick input.jpg output.png').
  2. If SVG is desired, rasterize it to PNG at the intended size first.
  3. In the renderer, filter the dialog strictly to PNG and validate the extension client-side before relying on the result.
  4. Show an inline hint that only PNG is accepted next to the icon picker.

Example fix

// before (user picks icon.jpg)
await ipc.invoke('shell:pickRepoIcon')

// after
// convert first: magick icon.jpg icon.png
await ipc.invoke('shell:pickRepoIcon') // now selects icon.png

// renderer-side guard:
const result = await ipc.invoke('shell:pickRepoIcon')
// (handler already enforces; surface its error as 'Please choose a PNG file.')
Defensive patterns

Strategy: validation

Validate before calling

import { extname } from 'path'

function isPngFile(filePath: string): boolean {
  return extname(filePath).toLowerCase() === '.png'
}

const picked = await pickFile()
if (picked && !isPngFile(picked)) {
  showError('Repo icons must be PNG files.')
  return
}
await ipc.invoke('shell:pickRepoIcon')

Type guard

import { extname } from 'path'
function isRepoIconPng(filePath: unknown): filePath is string {
  return typeof filePath === 'string' && extname(filePath).toLowerCase() === '.png'
}

Try / catch

try {
  await ipc.invoke('shell:pickRepoIcon')
} catch (e) {
  if (/PNG/.test((e as Error).message)) showError('Please choose a PNG file.')
  else throw e
}

Prevention

When it happens

Trigger: User selects a JPG, GIF, WEBP, SVG, or extensionless file in the picker. Even if the OS dialog shows only PNGs, on some platforms the filter is not enforced, so a user can pick another type. The extension lookup fails and the error is thrown before reading the file.

Common situations: Users with icons exported as JPG/SVG; renamed files whose content does not match the extension; OS dialog filter bypassed via drag path; users attempting to upload a logo in a non-PNG format.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/7ddf5f87a777e0ce. Report an issue: GitHub.