(ctx: SystemErrorContext)
| 154 | } |
| 155 | |
| 156 | async function _postSystemError(ctx: SystemErrorContext): Promise<void> { |
| 157 | const now = Date.now(); |
| 158 | const throttleKey = `system:${ctx.source}`; |
| 159 | const lastPosted = recentErrors.get(throttleKey) ?? 0; |
| 160 | if (now - lastPosted < SYSTEM_THROTTLE_MS) return; |
| 161 | pruneStaleEntries(now); |
| 162 | |
| 163 | const setting = await getCachedErrorChannel(); |
| 164 | if (!setting?.channel_id) { |
| 165 | logger.warn({ source: ctx.source, error: ctx.errorMessage.substring(0, 200) }, |
| 166 | 'System error occurred but no error_slack_channel configured — alert dropped'); |
| 167 | return; |
| 168 | } |
| 169 | |
| 170 | recentErrors.set(throttleKey, now); |
| 171 | |
| 172 | const quoted = ctx.errorMessage.substring(0, 500).split('\n').map(line => `> ${line}`).join('\n'); |
| 173 | const lines = [ |
| 174 | `:rotating_light: *System error: ${ctx.source}* [${processRole}]`, |
| 175 | '', |
| 176 | quoted, |
| 177 | ]; |
| 178 | |
| 179 | // See the matching comment in _postToolError — 'strict-public-only' |
| 180 | // keeps system-error alerting alive when Slack API is flaky. Drops |
| 181 | // only on confirmed drift. |
| 182 | const result = await sendChannelMessage( |
| 183 | setting.channel_id, |
| 184 | { text: lines.join('\n') }, |
| 185 | { requirePrivate: 'strict-public-only' }, |
| 186 | ); |
| 187 | if (result.skipped === 'not_private') { |
| 188 | logger.error( |
| 189 | { |
| 190 | source: ctx.source, |
| 191 | errorMessage: ctx.errorMessage.substring(0, 200), |
| 192 | event: 'error_channel_drift_silenced', |
| 193 | }, |
| 194 | 'System error suppressed because error_slack_channel is no longer private — admin must re-privatize or reconfigure. Original error lost to Slack but recorded here.', |
| 195 | ); |
| 196 | } |
| 197 | } |
no test coverage detected