Discord gateway partial config hot-reload can crash router timer
Summary
Saving Discord settings from the UI can hot-reload the Discord message router
with a partial gateway config that omits activeModeTiers. A background router
timer later reads activeModeTiers.rateWindowMinutes, throws a TypeError, and
can take down the daemon. The Discord Hub can then remain disconnected even
though Gateway is enabled in settings.
Observed locally
-
Settings showed gatewayEnabled: true.
-
/api/discord/status returned gatewayEnabled: true and connected: false.
-
Daemon stderr showed:
TypeError: Cannot read properties of undefined (reading 'rateWindowMinutes')
at MessageRouter.pruneStaleTimestamps (.../src/discord/router.ts:643:45)
-
The Hub disconnected-state copy said Not connected. Enable Gateway in Settings to connect. even when Gateway was already enabled.
Root Cause
updateDiscordGatewayConfig() built a merged config for persistence, but passed
the original partial browser payload into MessageRouter.updateConfig(). The UI
save payload does not include activeModeTiers, so the running router could lose
the nested defaults while still being typed as DiscordGatewayConfig.
Local Patch
- Added
normalizeDiscordGatewayConfig() to fill all gateway defaults,
including nested activeModeTiers.
- Used the normalizer when loading saved gateway config.
- Used the normalized config for server persistence and router/gateway
hot-reload.
- Added a defensive normalization step inside
MessageRouter constructor and
updateConfig().
- Updated the Discord Hub disconnected message to distinguish:
- Gateway disabled.
- Gateway enabled but no bot token.
- Gateway enabled but Discord still disconnected.
- Added
discord_gateway_config_test.ts covering normalizer defaults and the
partial hot-reload path.
Verification
deno fmt --check passed for touched files.
deno test -A --no-check packages/psycheros/tests/discord_gateway_config_test.ts
passed (2 passed).
- Plain
deno test -A ... is still blocked by a pre-existing Windows timer type
error in src/scheduler/scheduler.ts.
- Mirrored patch into the installed source under
C:\Users\rache\AppData\Roaming\Psycheros\source.
- Restarted the installed daemon and verified port
3000 responds.
- Live hot-reload verification:
- Posted a gateway config payload with
activeModeTiers removed.
- Server responded
{ "success": true }.
- Re-read config and confirmed
activeModeTiers.rateWindowMinutes was restored
to 60.
- Rendered
/fragments/discord in the in-app browser and confirmed the new
disconnected message appears while the old "Enable Gateway" copy does not.
Notes
The local saved bot token looked too short for a normal Discord bot token, so the
gateway may remain disconnected until a valid token is saved. That appears
separate from the router crash.
Discord gateway partial config hot-reload can crash router timer
Summary
Saving Discord settings from the UI can hot-reload the Discord message router
with a partial gateway config that omits
activeModeTiers. A background routertimer later reads
activeModeTiers.rateWindowMinutes, throws aTypeError, andcan take down the daemon. The Discord Hub can then remain disconnected even
though Gateway is enabled in settings.
Observed locally
Settings showed
gatewayEnabled: true./api/discord/statusreturnedgatewayEnabled: trueandconnected: false.Daemon stderr showed:
The Hub disconnected-state copy said
Not connected. Enable Gateway in Settings to connect.even when Gateway was already enabled.Root Cause
updateDiscordGatewayConfig()built a merged config for persistence, but passedthe original partial browser payload into
MessageRouter.updateConfig(). The UIsave payload does not include
activeModeTiers, so the running router could losethe nested defaults while still being typed as
DiscordGatewayConfig.Local Patch
normalizeDiscordGatewayConfig()to fill all gateway defaults,including nested
activeModeTiers.hot-reload.
MessageRouterconstructor andupdateConfig().discord_gateway_config_test.tscovering normalizer defaults and thepartial hot-reload path.
Verification
deno fmt --checkpassed for touched files.deno test -A --no-check packages/psycheros/tests/discord_gateway_config_test.tspassed (
2 passed).deno test -A ...is still blocked by a pre-existing Windows timer typeerror in
src/scheduler/scheduler.ts.C:\Users\rache\AppData\Roaming\Psycheros\source.3000responds.activeModeTiersremoved.{ "success": true }.activeModeTiers.rateWindowMinuteswas restoredto
60./fragments/discordin the in-app browser and confirmed the newdisconnected message appears while the old "Enable Gateway" copy does not.
Notes
The local saved bot token looked too short for a normal Discord bot token, so the
gateway may remain disconnected until a valid token is saved. That appears
separate from the router crash.