denoland/deno · error · ERR_INVALID_THIS

ERR_INVALID_THIS

ERR_INVALID_THIS

Error message

Value of "this" must be of type StringDecoder

What it means

StringDecoder stores its internal state under a private symbol (kBufferedBytes) that only the constructor initializes. StringDecoder.prototype.write() checks that the receiver carries that symbol and throws ERR_INVALID_THIS when it does not — i.e. the method was invoked on an object that was not built by `new StringDecoder(...)`. This mirrors Node's guard against borrowed or detached instance methods.

Source

Thrown at ext/node/polyfills/string_decoder.ts:328

      bufLen = 0;
      break;
  }
  this.encoding = normalizedEncoding;
  this.lastChar = Buffer.allocUnsafe(bufLen);
  this.enc = enc;
  this[kBufferedBytes] = 0;
  this[kMissingBytes] = 0;
  this.flush = flush;
  this.decode = decode;
}

StringDecoder.prototype.write = function write(buf) {
  if (typeof buf === "string") {
    return buf;
  }
  const normalizedBuf = normalizeBuffer(buf);
  if (this[kBufferedBytes] === undefined) {
    throw new ERR_INVALID_THIS("StringDecoder");
  }
  return this.decode(normalizedBuf);
};

StringDecoder.prototype.end = function end(buf) {
  let ret = "";
  if (buf !== undefined) {
    ret = this.write(buf);
  }
  if (this[kBufferedBytes] > 0) {
    ret += this.flush();
  }
  return ret;
};

StringDecoder.prototype.text = function text(buf, offset) {
  this[kBufferedBytes] = 0;
  this[kMissingBytes] = 0;

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Always construct with new StringDecoder(encoding) and invoke write/end on that exact instance
  2. When subclassing, call super(encoding) as the first statement of the constructor
  3. Bind methods before handing them to callbacks: emitter.on('data', decoder.write.bind(decoder)) or wrap in an arrow function
  4. If the value's origin is uncertain, check `instanceof StringDecoder` before calling write

Example fix

// before
socket.on('data', decoder.write); // `this` is the socket, not the decoder

// after
socket.on('data', (chunk) => decoder.write(chunk));
Defensive patterns

Strategy: type-guard

Type guard

import { StringDecoder } from 'node:string_decoder';

const isStringDecoder = (v) =>
  v instanceof StringDecoder &&
  typeof v.write === 'function' &&
  typeof v.end === 'function';

// before calling
if (!isStringDecoder(decoder)) {
  throw new TypeError('decoder must be constructed with new StringDecoder()');
}
return decoder.write(chunk);

Try / catch

try {
  out = decoder.write(chunk);
} catch (err) {
  if (err?.code === 'ERR_INVALID_THIS') {
    // call-site bug: fix the receiver (bind or wrap), do not retry the same call
    throw new Error('StringDecoder method called with wrong `this`; bind it to the instance');
  }
  throw err;
}

Prevention

When it happens

Trigger: StringDecoder.prototype.write.call({}, buf); creating an instance via Object.create(StringDecoder.prototype) instead of new; a subclass whose constructor forgets to call super(encoding); passing decoder.write as a bare callback so it is later invoked with a foreign `this`.

Common situations: Subclassing StringDecoder without super(); registering decoder.write directly as an event handler (emitter rebinds `this`); refactoring that destructures methods off the instance; mock/stub frameworks that re-invoke captured methods with a substitute receiver.

Related errors


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