moeru-ai/airi · error · Error

Screenshot must be a file smaller than 8 MiB.

Error message

Screenshot must be a file smaller than 8 MiB.

What it means

readImage() enforces a hard 8 MiB size limit (stat().size) and requires the target to be a regular file before base64-encoding it into a data URL. Oversized files are rejected to keep the in-memory base64 payload and the model context bounded.

Solutions

  1. Re-capture with a smaller region or use JPEG output to reduce size
  2. Check file size with fs.stat before calling readImage and downscale/compress if > 8 MiB
  3. Ensure the path is the actual screenshot file, not a directory

Example fix

// before
if ((await stat(p)).size > 8 * 1024 * 1024) { /* will throw */ }
await readImage({ path: p })
// after
const info = await stat(p)
if (info.size > 8 * 1024 * 1024) p = await reencodeAsJpeg(p)
await readImage({ path: p })
Defensive patterns

Strategy: validation

Validate before calling

import { stat } from 'node:fs/promises'
async function isReadableScreenshotSize(p: string): Promise<boolean> {
  const info = await stat(p)
  return info.isFile() && info.size <= 8 * 1024 * 1024
}

Try / catch

try {
  return await readImage({ path })
} catch (e) {
  if (e instanceof Error && e.message.includes('smaller than 8 MiB')) {
    const shrunk = await compressImage(path)
    return readImage({ path: shrunk })
  }
  throw e
}

Prevention

When it happens

Trigger: stat() reports a file larger than 8 * 1024 * 1024 bytes, or the path resolves to a directory/FIFO rather than a regular file.

Common situations: High-resolution multi-monitor captures saved as uncompressed PNG; reading a raw/bitmap dump instead of the CLI's encoded screenshot; pointing at a directory inside the store.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@438a067dde (2026-09-17). Data as JSON: /api/errors/79425a80393c19ef. Report an issue: GitHub.

Appendix: source

Thrown at apps/stage-tamagotchi/src/main/services/airi/computer-use/runtime.ts:146

      }
      catch {
        // Help output is text. Keep it intact for command discovery.
      }
      return { argv, exitCode, output, stderr }
    })
  }

  async function readImage(input: unknown): Promise<string> {
    const { path } = v.parse(v.object({ path: v.string() }), input)
    return enqueue(async () => {
      const root = await realpath(options.storeRoot)
      const target = await realpath(path)
      const within = relative(root, target)
      if (!within || isAbsolute(within) || within === '..' || within.startsWith(`..${sep}`))
        throw new Error('Only screenshots from this AIRI computer-use store can be read.')
      const info = await stat(target)
      if (!info.isFile() || info.size > 8 * 1024 * 1024)
        throw new Error('Screenshot must be a file smaller than 8 MiB.')
      const data = await readFile(target)
      const png = data.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))
      const jpeg = data[0] === 255 && data[1] === 216 && data[2] === 255
      if (!png && !jpeg)
        throw new Error('Screenshot must be PNG or JPEG.')
      return `data:image/${png ? 'png' : 'jpeg'};base64,${data.toString('base64')}`
    })
  }

  /** Rejects queued calls, aborts active work, and waits for the owned daemon to exit. */
  async function dispose() {
    disposed = true
    abort.abort()
    await queue
    try {
      await daemon?.stop()
    }
    finally {

View on GitHub (pinned to 438a067dde)