{"record":{"id":"079420bfe0aba731","repo":"discordjs/discord.js","slug":"token-has-already-been-set","errorCode":null,"errorMessage":"Token has already been set","messagePattern":"Token has already been set","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ws/src/ws/WebSocketManager.ts","lineNumber":376,"sourceCode":"\t\tawait this.updateShardCount(shardCount);\n\n\t\tconst shardIds = await this.getShardIds();\n\t\tconst data = await this.fetchGatewayInformation();\n\n\t\tif (data.session_start_limit.remaining < shardIds.length) {\n\t\t\tthrow new Error(\n\t\t\t\t`Not enough sessions remaining to spawn ${shardIds.length} shards; only ${\n\t\t\t\t\tdata.session_start_limit.remaining\n\t\t\t\t} remaining; resets at ${new Date(Date.now() + data.session_start_limit.reset_after).toISOString()}`,\n\t\t\t);\n\t\t}\n\n\t\tawait this.strategy.connect();\n\t}\n\n\tpublic setToken(token: string): void {\n\t\tif (this.#token) {\n\t\t\tthrow new Error('Token has already been set');\n\t\t}\n\n\t\tthis.#token = token;\n\t}\n\n\tpublic destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>) {\n\t\treturn this.strategy.destroy(options);\n\t}\n\n\tpublic send(shardId: number, payload: GatewaySendPayload) {\n\t\treturn this.strategy.send(shardId, payload);\n\t}\n\n\tpublic fetchStatus(): Awaitable<Collection<number, WebSocketShardStatus>> {\n\t\treturn this.strategy.fetchStatus();\n\t}\n\n\tpublic async [Symbol.asyncDispose]() {","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/discordjs/discord.js/blob/a81ed8a306d37fdc746e26a634b6a42164ba2c8c/packages/ws/src/ws/WebSocketManager.ts#L358-L394","documentation":"setToken() refuses to overwrite an existing token and throws if this.#token is already set. The manager treats the token as immutable once configured to prevent inconsistent auth state across shards.","triggerScenarios":"Calling manager.setToken(...) a second time on the same WebSocketManager instance, e.g. during a config reload or when reusing a cached manager instance.","commonSituations":"Hot-reloading configuration code that re-runs setToken(); accidentally constructing once globally but calling setToken in a per-request/per-event handler; unit tests reusing a module-level manager.","solutions":["Call setToken only once, at initialization","Destroy the existing manager and create a new one if the token must change","Guard the call: only setToken if no token was set yet (token getter throws when unset, so track it yourself)"],"exampleFix":"// before\nmanager.setToken(newToken); // throws if already set\n// after\ntry {\n  manager.setToken(newToken);\n} catch {\n  // token already set — destroy and rebuild if a change is truly required\n  await manager.destroy();\n  manager = new WebSocketManager(options);\n  manager.setToken(newToken);\n}","handlingStrategy":"validation","validationCode":"let tokenApplied = false;\nfunction safeSetToken(manager: WebSocketManager, token: string): void {\n  if (!tokenApplied) {\n    manager.setToken(token);\n    tokenApplied = true;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  manager.setToken(newToken);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('already been set')) {\n    // token is immutable; ignore or recreate the manager\n  } else throw e;\n}","preventionTips":["Set the token exactly once during bootstrap","For token rotation, destroy() the manager and build a new instance","Keep manager construction in a single init path, not in handlers"],"tags":["configuration","token","immutability"],"backgroundTag":"token-already-set","analyzedSha":"a81ed8a306d37fdc746e26a634b6a42164ba2c8c","analyzedAt":"2026-08-30T04:07:22.193Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}