fix(auto-delete): prevent double-processing + improve error classification

Two bugs causing 23 spurious 'error' logs after successful deletions:

1. batchProcessor switch missing 'completed' case: partitionBatchOutcome
   returns 'completed' for successful messages, but the switch only handled
   'upload_pending' and 'api_failed'. Successful messages fell through to
   default → re-enqueued to individual fallback → re-analyzed → re-delete
   attempt → error (message already gone from Discord). Now explicitly
   skips 'completed' messages.

2. isAlreadyDeletedError only caught codes 10008/404. Discord also returns
   10003 (Unknown Channel) and 50001 (Missing Access) when a message or
   channel is gone. Added these codes plus text-based fallback matching
   'Unknown Message'/'Unknown Channel'.

Impact: eliminates ~23 redundant error logs per day + stops wasted LLM
calls re-analyzing already-processed messages.
This commit is contained in:
asepharyana
2026-08-26 17:31:37 +07:00
parent 46d889271e
commit 54e7220d06
2 changed files with 22 additions and 1 deletions
@@ -130,7 +130,24 @@ function getErrorCode(error: unknown): number | string | undefined {
function isAlreadyDeletedError(error: unknown): boolean {
const code = getErrorCode(error);
return code === 10008 || code === 404 || code === "10008" || code === "404";
// Discord REST error codes for "message not found":
// 10008 = Unknown Message, 10003 = Unknown Channel,
// 50001 = Missing Access (channel deleted/hidden), 404 = HTTP
if (
code === 10008 ||
code === 10003 ||
code === 50001 ||
code === 404 ||
code === "10008" ||
code === "10003" ||
code === "50001" ||
code === "404"
)
return true;
// Fallback: check the message text for the Discord "Unknown Message" string
const msg =
error instanceof Error ? error.message : typeof error === "string" ? error : "";
return msg.includes("Unknown Message") || msg.includes("Unknown Channel");
}
function hasChannelMessagesApi(channel: unknown): channel is {
@@ -233,6 +233,10 @@ export async function processBatch(
// conversation cooldown instead of an immediate individual retry.
apiFailedMessages.push(msg);
break;
case "completed":
// Successfully analyzed — already broadcast + auto-delete scheduled
// above. Do NOT re-enqueue for individual fallback.
break;
default:
// incomplete / parse_failed / unexplained drops stay retryable via
// the individual fallback queue (same semantics as before).