cloudflare / cloudflare/workerd
🐛 Bug Report — Runtime APIs: Images binding crashes with "Cannot read properties of undefined (reading 'font')" when input() is given an ArrayBuffer
- Dominant language
- C++
- Stars
- 8.7k
- Forks
- 739
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 174
Description
## Summary
Since the rollout of the Images text rasterization feature (IMAGES-2451, #81681765 merged 2026-08-12), passing an `ArrayBuffer` to `env.IMAGES.input()` crashes every `.output()` call with:
```
TypeError: Cannot read properties of undefined (reading 'font')
at serializeTextSource (cloudflare-internal:images-api:14:30)
at cloudflare-internal:images-api:88:47
at withSpan (cloudflare-internal:tracing-helpers:37:20)
at ImageTransformerImpl.output (cloudflare-internal:images-api:82:22)
```
This worked fine before the rollout (our Worker ran unchanged in production since 2026-08-14 and broke around 2026-08-25 with no deploy on our side). Changing `compatibility_date` does not avoid it.
## Repro
```js
export default {
async fetch(request, env) {
const body = await request.arrayBuffer();
const result = await env.IMAGES.input(body) // ArrayBuffer input
.transform({ width: 1200 }) // any transform, or none
.output({ format: 'image/jpeg' }); // -> TypeError (reading 'font')
return result.response();
},
};
```
Any transform combination fails identically (plain resize, `segment`, etc.), and the error is thrown in ~60ms, before reaching the backend.
## Root cause
`isTextSource()` classifies the input source by elimination:
```ts
function isTextSource(source: ImageSource): source is TextRasterize {
return !(source instanceof ReadableStream);
}
```
An `ArrayBuffer` is not a `ReadableStream`, so it is misclassified as a text source, and `serializeTextSource()` dereferences `source.options.font` → `undefined.font` → TypeError.
## Expected behavior
Either:
1. Keep accepting raw bytes (`ArrayBuffer`/`TypedArray`) as before — the Images binding docs say `.input()` "Accepts image bytes up to 20 MB **from any source**", and the pre-2451 implementation passed the source straight through to the form serializer, so `ArrayBuffer` worked in practice; or
2. If only `ReadableStream` is supported (as the TS types say), throw a clear `ImagesError` (e.g. "input must be a ReadableStream") instead of an internal `TypeError` about `'font'` that gives no hint the input type is the problem.
A positive check for the text-source shape (e.g. `typeof source.content === 'string'`) instead of elimination against `ReadableStream` would fix both.
## Workaround
Wrap the buffer: `env.IMAGES.input(new Blob([body]).stream())`.
## Impact
Silent production breakage for any deployed Worker that passed bytes rather than a stream — no deploy on the user's side, not mitigated by `compatibility_date`, and the error message points at fonts rather than the input type, which makes it expensive to diagnose.
Contributor guide
Research direction
Start with the Images binding's isTextSource() and serializeTextSource() paths, then reproduce the failure using the ArrayBuffer example in the issue and compare it with the documented ReadableStream workaround. Done means ArrayBuffer or TypedArray input works again, or unsupported inputs produce a clear ImagesError instead of the font-related TypeError.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100