facebook/react · error · Error
582
582
Error message
Referenced Blob is not a Blob.
What it means
When decoding a client reply, a 'B' row points at a backing entry that was previously uploaded as a real FormData part and is referenced by id. The decoder fetches that entry and requires it to be a Blob/File. If the entry is absent or has been replaced by a string — the classic symptom of re-encoding the FormData through JSON — the instanceof check fails and this error is thrown.
Source
Thrown at packages/react-server/src/ReactFlightReplyServer.js:1886
DataView,
1,
obj,
key,
arrayRoot,
);
case 'B': {
// Blob
const id = parseInt(value.slice(2), 16);
const prefix = response._prefix;
const blobKey = prefix + id;
// We should have this backingEntry in the store already because we emitted
// it before referencing it. It should be a Blob.
const backingEntry: Blob = getBackingEntry(
response._formData,
blobKey,
) as any;
if (!(backingEntry instanceof Blob)) {
throw new Error('Referenced Blob is not a Blob.');
}
return backingEntry;
}
case 'R': {
return parseReadableStream(response, value, undefined, obj, key);
}
case 'r': {
return parseReadableStream(response, value, 'bytes', obj, key);
}
case 'X': {
return parseAsyncIterable(response, value, false, obj, key);
}
case 'x': {
return parseAsyncIterable(response, value, true, obj, key);
}
}
// We assume that anything else is a reference ID.
const ref = value.slice(1);View on GitHub (pinned to eafeac097b)
Solutions
- Forward the original request FormData unmodified to decodeReply (or use decodeReplyFromBusboy for multipart streams).
- If you must rebuild FormData, append the original File objects — not strings — under their original keys.
- Ensure a single Blob/File implementation is in play (pin one undici version, Node >= 20) so the instanceof check succeeds.
Example fix
// before — files flattened to strings const flat = Object.fromEntries(formData); const rebuilt = new FormData(); for (const [k, v] of Object.entries(flat)) rebuilt.append(k, v); const args = await decodeReply(rebuilt); // after — decode the original FormData const args = await decodeReply(formData);
Defensive patterns
Strategy: type-guard
Validate before calling
// Verify every expected file entry survived transport as a Blob, cross-realm safe.
export function assertIntactFileEntries(formData: FormData, fileKeys: string[]) {
for (const key of fileKeys) {
const v = formData.get(key);
if (v !== null && !isBlobLike(v)) throw new Error(`entry ${key} is no longer a Blob`);
}
} Type guard
// Duck-typed guard: survives cross-realm instances where instanceof Blob fails.
export function isBlobLike(v: unknown): v is Blob {
if (typeof v !== 'object' || v === null) return false;
const o = v as any;
return typeof o.stream === 'function' && typeof o.arrayBuffer === 'function' && typeof o.size === 'number';
} Try / catch
try {
const args = await decodeReply(formData);
} catch (e) {
if (/not a Blob/.test(String((e as Error)?.message))) {
return badRequest('upload payload corrupted in transit');
}
throw e;
} Prevention
- Never round-trip action FormData through JSON or Object.fromEntries.
- Use decodeReplyFromBusboy when consuming multipart directly from Node streams.
- Pin one undici/File implementation; avoid multiple Blob globals in the process.
When it happens
Trigger: decodeReply(formData) where the file row references an entry key that is missing or holds a string; rebuilding FormData via Object.fromEntries()/JSON round-trip before decoding; File objects from a different realm/implementation (two undici versions, mixed polyfills) so the instanceof Blob check fails even for real files.
Common situations: API gateways or middleware that re-serialize request bodies; selectively copying form fields and dropping the file key; manually constructed FormData in tests; Node runtimes with multiple File globals.
Related errors
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/99d9c7bf5e2a4bf4.
Report an issue: GitHub.