cheeriojs/cheerio · error · TypeError

Expected a string

Error message

Expected a string

What it means

cheerio's stream API (the Writable returned for streaming parsing) is created with decodeStrings: false, so every chunk written to it must already be a string. Writing a Buffer (or Uint8Array) hits the typeof chunk !== 'string' check and throws 'Expected a string'.

Source

Thrown at src/index.ts:88

  return load(str, opts);
}

function _stringStream(
  options: InternalOptions | undefined,
  cb: (err: Error | null | undefined, $: CheerioAPI) => void,
): Writable {
  if (options?._useHtmlParser2) {
    const parser = htmlparser2.createDocumentStream(
      (err, document) => cb(err, load(document, options)),
      options,
    );

    return new Writable({
      decodeStrings: false,
      write(chunk, _encoding, callback) {
        if (typeof chunk !== 'string') {
          throw new TypeError('Expected a string');
        }

        parser.write(chunk);
        callback();
      },
      final(callback) {
        parser.end();
        callback();
      },
    });
  }

  options ??= {};
  options.treeAdapter ??= htmlparser2Adapter;

  if (options.scriptingEnabled !== false) {
    options.scriptingEnabled = true;
  }

View on GitHub (pinned to a1be131f9b)

Solutions

  1. Call source.setEncoding('utf8') before piping the readable stream into cheerio
  2. Convert Buffers to strings in a transform: .pipe(new Transform({transform(c,e,cb){cb(null,c.toString())}}))
  3. If in the browser, decode bytes via TextDecoder before writing

Example fix

// before
fs.createReadStream('page.html').pipe(cheerioStream);
// after
fs.createReadStream('page.html', { encoding: 'utf8' }).pipe(cheerioStream);
Defensive patterns

Strategy: validation

Validate before calling

source.setEncoding('utf8'); // before pipe
// or when writing manually:
stream.write(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));

Type guard

const isStringChunk = (c: unknown): c is string => typeof c === 'string';

Try / catch

// Prefer validation over catching: decode strings before writing.
// If unavoidable:
try { stream.write(data); } catch (e) { if (e instanceof TypeError && /Expected a string/.test(e.message)) stream.write(String(data)); else throw e; }

Prevention

When it happens

Trigger: Piping a Buffer-producing stream (e.g. fs.createReadStream, http response with default encoding) into cheerio's stream without setting an encoding; calling stream.write(Buffer.from('<div>')) directly.

Common situations: Piping fs file streams or HTTP responses into the streaming parser; forgetting stream.setEncoding('utf8') before pipe; mixing Buffer-based pipelines with cheerio's string-only writable.

Related errors


AI-assisted analysis of cheeriojs/cheerio@a1be131f9b (2026-08-28). Data as JSON: /api/errors/ccda84c0d6057e0d. Report an issue: GitHub.