stablyai/orca · error · Error

Failed to encode resized clipboard image

Error message

Failed to encode resized clipboard image

What it means

Thrown by the clipboard-image resize path when `expo-image-manipulator` rendered and saved the resized PNG but returned an empty/falsy base64. Sending an empty base64 would pass downstream checks and upload a corrupt image, so the loader fails loudly instead. Source came from Clipboard.getImageAsync.

Source

Thrown at mobile/src/session/use-mobile-terminal-paste.ts:39

  const base64 = source.replace(CLIPBOARD_IMAGE_DATA_URL_PREFIX_RE, '')
  const file = new FsFile(Paths.cache, `orca-clip-resize-${Date.now()}.png`)
  let context: ReturnType<typeof ImageManipulator.manipulate> | null = null
  let rendered: Awaited<
    ReturnType<ReturnType<typeof ImageManipulator.manipulate>['renderAsync']>
  > | null = null
  let resultUri: string | null = null
  try {
    file.create({ overwrite: true })
    file.write(base64, { encoding: 'base64' })
    context = ImageManipulator.manipulate(file.uri)
    context.resize({ width: target.width, height: target.height })
    rendered = await context.renderAsync()
    const result = await rendered.saveAsync({ format: SaveFormat.PNG, base64: true })
    resultUri = result.uri
    // Why: empty base64 would pass the downstream base64 check and upload a corrupt
    // image, so fail loudly here instead of silently sending an invalid payload.
    if (!result.base64) {
      throw new Error('Failed to encode resized clipboard image')
    }
    return { data: result.base64, width: result.width, height: result.height }
  } finally {
    rendered?.release()
    context?.release()
    if (resultUri) {
      try {
        new FsFile(resultUri).delete()
      } catch {
        // Best-effort cleanup; ImageManipulator saves into cache for every retry.
      }
    }
    try {
      file.delete()
    } catch {
      // Best-effort cleanup; the OS reclaims the cache directory regardless.
    }
  }

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Retry the paste (often transient under memory pressure).
  2. Use a smaller source image (downsample before paste).
  3. Update expo-image-manipulator.
  4. Fall back to text paste / save image and attach manually.

Example fix

// before
const result = await rendered.saveAsync({ format: SaveFormat.PNG, base64: true })
if (!result.base64) {
  throw new Error('Failed to encode resized clipboard image')
}
// after - re-read the saved file as base64 when saveAsync omits it
const result = await rendered.saveAsync({ format: SaveFormat.PNG, base64: true })
let base64 = result.base64
if (!base64 && result.uri) {
  base64 = await FsFile.read(result.uri, { encoding: 'base64' })
}
if (!base64) {
  throw new Error('Failed to encode resized clipboard image')
}
Defensive patterns

Strategy: validation

Validate before calling

const target = fitWithin(source, { width: 1024, height: 1024 }) // downsample before resize

Type guard

function hasBase64(r: { base64?: string } | null): r is { base64: string } {
  return !!r && typeof r.base64 === 'string' && r.base64.length > 0
}

Try / catch

try {
  return await resizeMobileClipboardImage(source, target)
} catch {
  showToast('Could not process clipboard image', 1500)
  return null
}

Prevention

When it happens

Trigger: ImageManipulator produced a valid file (uri set) but no base64 string - seen on some iOS/Android versions when base64:true is passed but the encoded output is empty (memory pressure, very large source, or a platform bug).

Common situations: Pasting a very high-res screenshot/photo that survives decode but fails re-encode; low-memory device; expo-image-manipulator version regression; iOS HEIC source that decodes oddly.

Related errors


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