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
- Always construct with new StringDecoder(encoding) and invoke write/end on that exact instance
- When subclassing, call super(encoding) as the first statement of the constructor
- Bind methods before handing them to callbacks: emitter.on('data', decoder.write.bind(decoder)) or wrap in an arrow function
- 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
- Only call write/end on the object returned by new StringDecoder(encoding)
- Bind methods when passing them as callbacks: emitter.on('data', decoder.write.bind(decoder))
- In subclasses, call super(encoding) first in the constructor
- Treat ERR_INVALID_THIS as a call-shape bug — fix the call site instead of adding data checks
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
- ERR_HTTP2_TRAILERS_ALREADY_SENT
- ERR_HTTP2_TRAILERS_NOT_READY
- ERR_HTTP2_NESTED_PUSH
- ERR_UNKNOWN_ENCODING
- ERR_UNKNOWN_ENCODING
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/68e7e367f7e2aac9.
Report an issue: GitHub.