expo/expo · error · RangeError

Unknown encoding: ${label} (normalized: ${normalizedLabel})

Error message

Unknown encoding: ${label} (normalized: ${normalizedLabel})

What it means

expo's TextDecoder polyfill intentionally supports only the UTF-8 family of labels (utf-8, utf8, unicode20utf8, x-unicode20utf8), matching the WHATWG encoding standard's must-support encoding. Any other label throws a RangeError with both the original and the normalized label included, mirroring the spec's RangeError for unknown encodings.

Source

Thrown at packages/expo/src/winter/TextDecoder.ts:24

  ignoreBOM: boolean;
  fatal: boolean;
  bomSeen: boolean;
  pending: Uint8Array;
  streaming: boolean;
}

function assertValidUTF8Label(label: unknown): void {
  const normalizedLabel = String(label).trim().toLowerCase();
  switch (normalizedLabel) {
    case 'unicode-1-1-utf-8':
    case 'unicode11utf8':
    case 'unicode20utf8':
    case 'utf-8':
    case 'utf8':
    case 'x-unicode20utf8':
      return;
    default:
      throw new RangeError(`Unknown encoding: ${label} (normalized: ${normalizedLabel})`);
  }
}

function normalizeBytes(input: ArrayBuffer | ArrayBufferView | undefined): Uint8Array {
  if (input === undefined) {
    return EMPTY_BYTES;
  } else if (input instanceof Uint8Array) {
    return input;
  } else if (ArrayBuffer.isView(input)) {
    return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
  } else if (
    (input[Symbol.toStringTag] as string) === 'ArrayBuffer' ||
    (input[Symbol.toStringTag] as string) === 'SharedArrayBuffer'
  ) {
    return new Uint8Array(input as ArrayBuffer);
  } else {
    throw new TypeError('The input must be an ArrayBuffer or ArrayBufferView');
  }

View on GitHub (pinned to da586c407b)

Solutions

  1. Decode as UTF-8: `new TextDecoder('utf-8')` - convert the data to UTF-8 upstream if it is not already.
  2. Transcode the bytes to UTF-8 before decoding (server-side or with a dedicated converter).
  3. On hosts with a native full TextDecoder (browsers, Node), use globalThis.TextDecoder instead of the polyfill.

Example fix

// before
const dec = new TextDecoder('utf-16le'); // RangeError: Unknown encoding

// after
const dec = new TextDecoder('utf-8'); // only UTF-8 is supported
Defensive patterns

Strategy: validation

Validate before calling

const UTF8_LABELS = new Set(['utf-8', 'utf8', 'unicode20utf8', 'x-unicode20utf8']);

function isSupportedLabel(label: string): boolean {
  return UTF8_LABELS.has(label.trim().toLowerCase().replace(/[-_]/g, '')); // rough normalize
}

if (!isSupportedLabel(label)) label = 'utf-8';

Try / catch

try {
  decoder = new TextDecoder(label);
} catch (error) {
  if (error instanceof RangeError && error.message.startsWith('Unknown encoding')) {
    decoder = new TextDecoder('utf-8'); // fall back to UTF-8
  } else throw error;
}

Prevention

When it happens

Trigger: `new TextDecoder('utf-16le')`, `new TextDecoder('iso-8859-1')`, `new TextDecoder('windows-1252')` - any label that does not normalize to a UTF-8 alias - on a platform using expo's TextDecoder (e.g. React Native runtimes without a native TextDecoder).

Common situations: Porting web code that decodes UTF-16 response bodies; libraries assuming Node's full-ICU decoder is present; labels taken verbatim from a server's charset header.

Related errors


AI-assisted analysis of expo/expo@da586c407b (2026-08-23). Data as JSON: /api/errors/1176637ea3666f4b. Report an issue: GitHub.