redis/node-redis · critical · Error
Unknown RESP type
Error message
Unknown RESP type ${type} "${String.fromCharCode(type)}" What it means
Thrown from the default branch of the RESP3 decoder's top-level decodeReply switch. Every well-formed reply must start with a known type byte (NULL, BOOLEAN, NUMBER, DOUBLE, BIG_NUMBER, SIMPLE_STRING, BLOB_STRING, VERBATIM_STRING, SIMPLE_ERROR, BLOB_ERROR, ARRAY, SET, MAP, PUSH). An unrecognized leading byte means the byte stream is not valid RESP3 at the reply boundary. The message prints both the numeric code and its ASCII char for diagnostics.
Solutions
- Verify the endpoint is a real Redis: redis-cli -h <host> -p <port> PING.
- Stop forcing RESP: 3 in options; let the client negotiate the highest mutually supported version.
- Confirm TLS settings (socket.tls / rediss://) match the server's actual mode.
- If the byte is consistent and reproducible, capture it and file a client bug with the command that triggered it.
Example fix
// before
const client = createClient({ url: 'redis://...', RESP: 3 });
// after
const client = createClient({ url: 'redis://...' }); // let the client negotiate RESP version Defensive patterns
Strategy: try-catch
Validate before calling
import { createClient } from 'redis';
async function assertRedisEndpoint(url) {
const probe = createClient({ url });
probe.on('error', () => {});
await probe.connect();
const pong = await probe.ping();
await probe.disconnect();
if (pong !== 'PONG') throw new Error(`endpoint ${url} did not PONG`);
}
await assertRedisEndpoint('redis://host:6379'); Try / catch
const client = createClient({ url });
client.on('error', err => {
if (/Unknown RESP type/.test(err.message)) {
console.error('Stream desync detected; reconnecting', err);
}
});
try {
await client.connect();
} catch (err) {
// surface protocol mismatch before the app runs commands
throw err;
} Prevention
- Validate the endpoint with redis-cli before pointing the app at it.
- Do not force RESP: 3 on redis servers older than 6.0.
- Keep client and server major versions aligned.
- Confirm TLS mode (rediss:// vs redis://) matches the server.
When it happens
Trigger: Connecting the client (RESP3 mode) to something that is not a Redis server; a proxy injecting non-RESP bytes; RESP2-only server where the HELLO/3 negotiation left the stream misaligned; truncated or corrupted reply mid-frame; TLS plaintext/encrypted mismatch producing garbage.
Common situations: Pointing the url at a wrong service port (e.g. an HTTP or stats endpoint); HAProxy/nginx misroute; rediss:// against a plaintext port or vice-versa; redis server older than 6.0 with RESP forced to 3; a custom command whose reply shape the decoder cannot parse.
Related errors
- All the root nodes are unavailable
- Client Side Caching is only supported with RESP3
- Client Side Caching is only supported with RESP3
- Client Side Caching is only supported with RESP3
- Cluster already open
AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11).
Data as JSON: /api/errors/613035d4c3504556.
Report an issue: GitHub.
Appendix: source
Thrown at packages/client/lib/RESP/decoder.ts:234
return this.#handleDecodedValue(
this.onReply,
this.#decodeSet(this.getTypeMapping(), chunk)
);
case RESP_TYPES.MAP:
return this.#handleDecodedValue(
this.onReply,
this.#decodeMap(this.getTypeMapping(), chunk)
);
case RESP_TYPES.PUSH:
return this.#handleDecodedValue(
this.onPush,
this.#decodeArray(PUSH_TYPE_MAPPING, chunk)
);
default:
throw new Error(`Unknown RESP type ${type} "${String.fromCharCode(type)}"`);
}
}
#handleDecodedValue(cb, value) {
if (typeof value === 'function') {
this.#next = this.#continueDecodeValue.bind(this, cb, value);
return true;
}
cb(value);
return false;
}
#continueDecodeValue(cb, next, chunk) {
this.#next = undefined;
return this.#handleDecodedValue(cb, next(chunk));
}
View on GitHub (pinned to 90fd0652bc)