Skip to content

[Bug]: unsupported or oversized image attachment aborts the entire prompt (HTTP 400) in the user-message path #43262

Description

@weike-zhang

Summary

Attaching an image in a format the Photon-based resizer cannot decode (e.g. AVIF, HEIC, BMP, TIFF) — or an image too large to be resized below the inline limit — makes the entire prompt request fail with a 400 instead of degrading gracefully.

Expected Behavior

A single undecodable/oversized image attachment should not abort the whole message. It should be dropped (or replaced with a text note) while the rest of the prompt still sends — exactly like the tool-result path does today.

Current Behavior

packages/opencode/src/session/prompt.ts (user-message path, createUserMessage) runs image.normalize(part) inside a plain Effect.forEach that only catches Image.ResizerUnavailableError:

const parts = yield* Effect.forEach(resolvedParts, (part) =>
  part.type === "file" && part.mime.startsWith("image/")
    ? image.normalize(part).pipe(
        Effect.catchIf(
          (error) => error instanceof Image.ResizerUnavailableError,
          () => Effect.succeed(part),
        ),
      )
    : Effect.succeed(part),
)

Effect.forEach fails fast. If image.normalize throws Image.DecodeError (unsupported format) or Image.SizeError (cannot be resized below max_base64_bytes), the whole createUserMessage effect fails → promptSvc.prompt fails → the HTTP handler (httpapi/handlers/session.ts) maps it to a 400 Bad Request. The image message can never be sent.

Verified with the real @silvia-odwyer/photon-node@0.3.4 dependency: PhotonImage.new_from_byteslice throws for AVIF, HEIC, and BMP byte inputs, while PNG/JPEG/WebP decode fine.

The tool-result path (processor.ts) already handles this correctly with Effect.exit + filtering + an [N image omitted...] note. The user-message path is the asymmetric one.

Steps to Reproduce

  1. In the TUI, paste a local .avif (or .heic / .bmp) image path — the TUI attachment picker explicitly accepts .avif (packages/tui/src/component/prompt/local-attachment.ts).
  2. Send the prompt with the attached image.
  3. The request fails with HTTP 400; the message is never sent.

Impact

Any user with a modern camera/phone screenshot (HEIC) or an AVIF screenshot, or any image that cannot be compressed under the 5MB inline limit, hits a hard failure with no way to send the message at all.

Suggested Fix

Mirror the processor.ts pattern: catch normalize failures per-part (Effect.exit / Effect.catchAll), drop or replace the failing image with a synthetic text note, and keep the prompt alive.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions