denoland/deno · error · TypeError

A chunk may only be either a string or an Uint8Array

Error message

A chunk may only be either a string or an Uint8Array

What it means

The writable side of a WebSocketStream only accepts strings and Uint8Arrays: the write() implementation checks typeof chunk === 'string' or the typed-array tag 'Uint8Array'. Anything else - ArrayBuffer, DataView, other typed arrays, Blob, plain objects, numbers - throws a plain TypeError from inside the stream.

Source

Thrown at ext/websocket/02_websocketstream.js:224

                const err = new WebSocketError("Closed while connecting");
                this[_opened].reject(err);
                this[_closed].reject(err);
              },
            );
          } else {
            this[_rid] = create.rid;

            const writable = new WritableStream({
              write: async (chunk) => {
                if (typeof chunk === "string") {
                  await op_ws_send_text_async(this[_rid], chunk);
                } else if (
                  TypedArrayPrototypeGetSymbolToStringTag(chunk) ===
                    "Uint8Array"
                ) {
                  await op_ws_send_binary_async(this[_rid], chunk);
                } else {
                  throw new TypeError(
                    "A chunk may only be either a string or an Uint8Array",
                  );
                }
              },
              close: async () => {
                this.close();
                await this.closed;
              },
              abort: async (reason) => {
                let closeCode = null;
                let reasonString = "";

                if (
                  ObjectPrototypeIsPrototypeOf(WebSocketErrorPrototype, reason)
                ) {
                  closeCode = reason.closeCode;
                  reasonString = reason.reason;
                }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Encode text to bytes: new TextEncoder().encode(str)
  2. Wrap buffers: new Uint8Array(arrayBuffer)
  3. Stringify structured data: JSON.stringify(value)
  4. Convert Blob: new Uint8Array(await blob.arrayBuffer())

Example fix

// before
await writer.write({ type: 'msg' });
await writer.write(await file.arrayBuffer()); // ArrayBuffer

// after
await writer.write(JSON.stringify({ type: 'msg' }));
await writer.write(new Uint8Array(await file.arrayBuffer()));
Defensive patterns

Strategy: type-guard

Validate before calling

function toChunk(v: unknown): string | Uint8Array {
  if (typeof v === 'string') return v;
  if (v instanceof Uint8Array) return v;
  if (v instanceof ArrayBuffer) return new Uint8Array(v);
  if (typeof v === 'object' && v !== null) return JSON.stringify(v);
  throw new TypeError('unsupported chunk type');
}
await writer.write(toChunk(data));

Type guard

const isWsChunk = (v: unknown): v is string | Uint8Array =>
  typeof v === 'string' || v instanceof Uint8Array;

Prevention

When it happens

Trigger: writer.write(new ArrayBuffer(8)), writer.write(new Uint16Array(4)), writer.write(blob) (allowed on WebSocket.send but not here), writer.write({ data }) without JSON.stringify, writer.write(42).

Common situations: Assuming ws.send()-compatible types carry over to WebSocketStream; forgetting JSON.stringify for objects; passing a raw ArrayBuffer from file/socket reads instead of a Uint8Array view.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/4e9bbafbf38c01b3. Report an issue: GitHub.