[Bug]: checkBan throws "key.startsWith is not a function" and answers 500 on /oauth when USE_REDIS is off
What happened?
Every social (Google) sign-in throws a TypeError inside checkBan and the request under /oauth is answered with 500 instead of the normal response. The login itself still completes — the user document and a refresh-token session are created a few milliseconds before the error — so the visible damage is a 500 on the OAuth path plus a permanent error line on every login.
Root cause: checkBan hands Keyv a non-string key when USE_REDIS is off.
api/server/middleware/checkBan.js:
const banCache = new Keyv({ store: keyvMongo, namespace: ViolationTypes.BAN, ttl: 0 });
const getBanCacheKey = (prefix, value, useRedis) => {
if (!value) return '';
return useRedis ? `ban_cache:${prefix}:${value}` : value; // <- raw value, not a string
};
req.ip = removePorts(req);
let userId = req.user?.id ?? req.user?._id ?? null; // <- ObjectId on the OAuth path
const userKey = getBanCacheKey('user', userId, useRedis);
const [cachedIPBan, cachedUserBan] = await Promise.all([
ipKey ? banCache.get(ipKey) : undefined,
userKey ? banCache.get(userKey) : undefined, // <- throws here (line 108)
]);With USE_REDIS unset, useRedis is false, so userKey is the raw userId. On the OAuth path req.user carries an ObjectId rather than a string, and keyv 5.6.0's _getKeyPrefix does key.startsWith(...):
_getKeyPrefix(key) {
if (!this._useKeyPrefix) { return key; }
if (!this._namespace) { return key; }
if (key.startsWith(`${this._namespace}:`)) { return key; } // TypeError for an ObjectId
return `${this._namespace}:${key}`;
}ViolationTypes.BAN resolves to the string "ban", so _namespace is fine — the offending value is the key.
Reproduced in isolation inside the released image (not an inference — run against the real keyvMongo store and the real ViolationTypes):
const { Keyv } = require('keyv');
const { keyvMongo } = require('@librechat/api');
const { ViolationTypes } = require('librechat-data-provider');
const { Types } = require('mongoose');
const banCache = new Keyv({ store: keyvMongo, namespace: ViolationTypes.BAN, ttl: 0 });
// get('some-string') -> OK
// get(new Types.ObjectId()) -> TypeError: key.startsWith is not a function
// get(12345) -> TypeError: key.startsWith is not a function
// getBanCacheKey('user', ObjectId, false) -> typeof "object" (USE_REDIS=false)
// getBanCacheKey('user', ObjectId, true) -> typeof "string" (USE_REDIS=true does not fail)Suggested fix: make the key a string before it reaches Keyv, e.g. getBanCacheKey('user', userId != null ? String(userId) : '', useRedis), or drop the useRedis ? ... : value branch and always build a prefixed string, since the raw-value branch is what breaks. (Note the same function is also used for req.ip; that one is a string today, so it does not fail — the object case is the user key.)
Version Information
LibreChat: v0.8.8-rc2
Image: ghcr.io/danny-avila/librechat:v0.8.8-rc2@sha256:9e5266c6b83fa68f69f35ef3a81b4388c20ee3a83a942c428de53a221e9c8583
keyv: 5.6.0 (from node_modules inside the image)
Node.js: v24.16.0
Cache/store: keyv-mongo, Redis NOT used (USE_REDIS unset)
BAN_VIOLATIONS=true (default)
ALLOW_SOCIAL_LOGIN=true, ALLOW_SOCIAL_REGISTRATION=true (Google)
Provider: custom OpenAI-compatible endpoint (unrelated to this bug)
Deployment: docker compose, MongoDB 8.0Checked main before filing: api/server/middleware/checkBan.js is unchanged in every line quoted above (last commit touching the file is 146664e0), so this is not fixed on main today.
Steps to Reproduce
- Run LibreChat with
BAN_VIOLATIONS=true(default) and no Redis (USE_REDISunset). - Enable a social login (Google) and sign in with it.
docker logs <librechat-container>→ the TypeError fires 7–9 ms after the user document is created, with"request_path":"/oauth"and status 500.
The same failure can be triggered deterministically with the 12-line script above (it needs no OAuth flow at all).
Relevant log output
{"level":"error","message":"key.startsWith is not a function","name":"TypeError",
"requestId":"5ab20394-2831-4e25-b7b0-40de993bf3c0","request_method":"GET","request_path":"/oauth",
"stack":"TypeError: key.startsWith is not a function
at Keyv._getKeyPrefix (/app/node_modules/keyv/dist/index.cjs:499:13)
at Keyv.get (/app/node_modules/keyv/dist/index.cjs:527:71)
at checkBan (/app/api/server/middleware/checkBan.js:108:26)
at /app...","timestamp":"2026-09-16T15:58:15.356Z"}Correlation with user creation (same deployment, two separate accounts):
15:58:15.347Z user created (provider: google)
15:58:15.356Z TypeError on /oauth (+9 ms)
16:20:16.598Z user created (provider: google)
16:20:16.605Z TypeError on /oauth (+7 ms)Isolated experiment output inside the same image:
namespace = "ban"
OK string (req.ip)
THROW ObjectId (req.user._id) -> TypeError: key.startsWith is not a function
THROW number -> TypeError: key.startsWith is not a function
--- what checkBan builds ---
ipKey typeof (USE_REDIS=false) = string
userKey typeof (USE_REDIS=false) = object
userKey typeof (USE_REDIS=true) = stringWhat browsers are you seeing the problem on?
Server-side, provider-independent. Reproduced with Chrome 152 as the client; no browser-specific behaviour is involved.
Source: danny-avila/LibreChat