{"record":{"id":"fa533cced956d87a","repo":"redis/node-redis","slug":"invalid-sentinelclientoptions-for-sentinel","errorCode":null,"errorMessage":"invalid sentinelClientOptions for Sentinel","messagePattern":"invalid sentinelClientOptions for Sentinel","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/sentinel/index.ts","lineNumber":885,"sourceCode":"    // a failover must be transparently re-preparable on the promoted master's connections.\n    // (Sentinel-monitor clients use #sentinelClientOptions and never run HIMPORT.)\n    this.#nodeClientOptions.himportRegistry = new FieldsetRegistry();\n\n    if (options.clientSideCache) {\n      if (options.clientSideCache instanceof PooledClientSideCacheProvider) {\n        this.#clientSideCache = this.#nodeClientOptions.clientSideCache = options.clientSideCache;\n      } else {\n        const cscConfig = options.clientSideCache;\n        this.#clientSideCache = this.#nodeClientOptions.clientSideCache = new BasicPooledClientSideCache(cscConfig);\n//        this.#clientSideCache = this.#nodeClientOptions.clientSideCache = new PooledNoRedirectClientSideCache(cscConfig);\n      }\n    }\n\n    this.#sentinelClientOptions = options.sentinelClientOptions ? Object.assign({} as RedisClientOptions<typeof RedisSentinelModule, F, S, RESP, TYPE_MAPPING, RedisTcpSocketOptions>, options.sentinelClientOptions) : {};\n    this.#sentinelClientOptions.modules = RedisSentinelModule;\n\n    if (this.#sentinelClientOptions.url !== undefined) {\n      throw new Error(\"invalid sentinelClientOptions for Sentinel\");\n    }\n\n    this.#masterClientQueue = new WaitQueue();\n    for (let i = 0; i < this.#masterPoolSize; i++) {\n      this.#masterClientQueue.push(i);\n    }\n\n    /* persistent object for life of sentinel object */\n    this.#pubSubProxy = new PubSubProxy(\n      this.#nodeClientOptions,\n      err => this.emit('error', err)\n    );\n  }\n\n  #createClient(\n    node: RedisNode,\n    clientOptions: AnyRedisClientOptions,\n    reconnectStrategy?: false","sourceCodeStart":867,"sourceCodeEnd":903,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/sentinel/index.ts#L867-L903","documentation":"Symmetric to the nodeClientOptions guard: Sentinel derives the addresses of the sentinel-monitor processes from `rootNodes`/discovery, so a hard-coded `url` in `sentinelClientOptions` is meaningless and rejected. Only the sentinel-monitor client options are validated here (after the sentinel module is forced onto them).","triggerScenarios":"Passing `sentinelClientOptions: { url: 'redis://...' }` to `new RedisSentinel(...)`. Throws synchronously in the constructor.","commonSituations":"Sharing one options object between nodeClientOptions and sentinelClientOptions where a url slipped in; copy-pasting a standalone client URL into the sentinel monitor config.","solutions":["Remove `url` from `sentinelClientOptions`; supply sentinel endpoints via `rootNodes`.","Keep only auth/socket fields (e.g. `password`, TLS) in `sentinelClientOptions`.","If you need a single sentinel endpoint, put it in `rootNodes: [{ host, port }]`."],"exampleFix":"// before\nnew RedisSentinel({ name: 'mymaster', sentinelClientOptions: { url: 'redis://sentinel:26379' } }, id);\n// after\nnew RedisSentinel({ name: 'mymaster', rootNodes: [{ host: 'sentinel', port: 26379 }], sentinelClientOptions: { password: 'sentinel-secret' } }, id);","handlingStrategy":"validation","validationCode":"function assertSentinelOpts(opts: { sentinelClientOptions?: { url?: unknown } }) {\n  if (opts.sentinelClientOptions && opts.sentinelClientOptions.url !== undefined) {\n    throw new Error('sentinelClientOptions.url is forbidden for Sentinel; use rootNodes');\n  }\n}\nassertSentinelOpts(opts);","typeGuard":"function hasNoSentinelUrl(opts: { sentinelClientOptions?: { url?: unknown } }): boolean {\n  return opts.sentinelClientOptions?.url === undefined;\n}","tryCatchPattern":"try { new RedisSentinel(opts, id); }\ncatch (e) {\n  if (String(e).includes('invalid sentinelClientOptions')) {\n    const { url, ...rest } = opts.sentinelClientOptions ?? {};\n    opts = { ...opts, sentinelClientOptions: rest };\n    new RedisSentinel(opts, id);\n  } else throw e;\n}","preventionTips":["Keep sentinel endpoint addresses only in rootNodes.","Store auth/TLS settings separately from URL-bearing option templates."],"tags":["sentinel","configuration","url","constructor","sentinel-options"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}