{"record":{"id":"3afd2d6199c09503","repo":"affaan-m/ECC","slug":"invalid-interaction-id-token","errorCode":null,"errorMessage":"invalid interaction id/token","messagePattern":"invalid interaction id/token","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/discord/ecc-bot.mjs","lineNumber":45,"sourceCode":"const API = 'https://discord.com/api/v10';\n\n// Strip CR/LF from string args so Discord-payload-controlled values (command\n// names, usernames) cannot forge or inject extra log lines (log injection).\nconst log = (...a) =>\n  console.log(new Date().toISOString(), ...a.map(x => (typeof x === 'string' ? x.replace(/[\\r\\n]+/g, ' ') : x)));\n\n// Interaction ids are Discord snowflakes (numeric) and tokens are a bounded\n// URL-safe set. Validate before building the callback URL so a malformed or\n// hostile gateway payload cannot inject path segments / alter the request\n// target (SSRF). The host is always the fixed API constant.\nconst SNOWFLAKE_RE = /^[0-9]{1,20}$/;\nconst INTERACTION_TOKEN_RE = /^[A-Za-z0-9._-]{1,255}$/;\n\nfunction interactionCallbackUrl(interaction) {\n  const id = String(interaction?.id ?? '');\n  const token = String(interaction?.token ?? '');\n  if (!SNOWFLAKE_RE.test(id) || !INTERACTION_TOKEN_RE.test(token)) {\n    throw new Error('invalid interaction id/token');\n  }\n  return `${API}/interactions/${id}/${token}/callback`;\n}\n\n// Clamp a remote-supplied timer interval to a sane range so a hostile/bogus\n// heartbeat_interval cannot spin a tight loop or hang the bot (resource\n// exhaustion). Discord's real value is ~41250ms.\nfunction clampHeartbeatInterval(value) {\n  const n = Number(value);\n  if (!Number.isFinite(n)) return 41250;\n  return Math.max(1000, Math.min(n, 600000));\n}\n\n// ---------- skill + docs lookup (local clone as the data source) ----------\n\nfunction parseFrontmatter(text) {\n  const m = text.match(/^---\\n([\\s\\S]*?)\\n---/);\n  if (!m) return {};","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/discord/ecc-bot.mjs#L27-L63","documentation":"interactionCallbackUrl builds the Discord interactions callback endpoint from an interaction's id and token. The id must be a valid snowflake and the token must match /^[A-Za-z0-9._-]{1,255}$/; otherwise it throws 'invalid interaction id/token' rather than issuing a request to a malformed endpoint. This protects against hostile or corrupted payloads delivered by the Discord gateway/HTTP flow.","triggerScenarios":"Handling an interaction object that is missing id or token (undefined), contains characters outside the allowed set (URL-encoded or padded values), or was reconstructed from a log/queue where fields were truncated or serialized incorrectly.","commonSituations":"Replaying interactions from a dead-letter queue with lossy serialization; forwarding interactions between services and dropping optional fields; processing a malformed/legacy interaction payload; test harnesses feeding hand-built interaction fixtures.","solutions":["Log (safely, without printing the token) the interaction id and token presence before building the URL to see which field fails validation.","Use interaction.id and interaction.token exactly as delivered by Discord's payload; do not re-encode or trim them.","Fix the serialization path so id/token survive transit (store as strings, not numbers, to avoid precision loss on snowflakes).","Drop and re-acknowledge corrupt interactions instead of retrying the callback."],"exampleFix":"// before\nconst url = interactionCallbackUrl({ id: payload.d.interaction_id, token: payload.d.interaction_token });\n// after (use the nested interaction object as delivered)\nconst interaction = payload.d.interaction ?? payload.d;\nconst url = interactionCallbackUrl(interaction);","handlingStrategy":"type-guard","validationCode":"const SNOWFLAKE_RE = /^\\d{17,20}$/;\nconst TOKEN_RE = /^[A-Za-z0-9._-]{1,255}$/;\nfunction canBuildCallbackUrl(interaction) {\n  return SNOWFLAKE_RE.test(String(interaction?.id ?? '')) && TOKEN_RE.test(String(interaction?.token ?? ''));\n}\nif (!canBuildCallbackUrl(interaction)) return; // skip/drop corrupt interaction","typeGuard":"const hasValidInteraction = (i) =>\n  typeof i === 'object' && i !== null &&\n  /^\\d+$/.test(String(i.id ?? '')) &&\n  typeof i.token === 'string' && /^[A-Za-z0-9._-]{1,255}$/.test(i.token);","tryCatchPattern":"try {\n  await fetch(interactionCallbackUrl(interaction), init);\n} catch (err) {\n  if (err.message === 'invalid interaction id/token') {\n    console.warn(`Dropping interaction ${interaction?.id ?? '(unknown id)'}: malformed id/token`);\n    return;\n  }\n  throw err;\n}","preventionTips":["Serialize snowflake ids as strings end-to-end to avoid precision loss.","Never re-encode or trim interaction tokens when forwarding between services.","Validate interaction payloads at the ingest boundary before queueing/replaying.","Keep tokens out of logs; log only the id when debugging."],"tags":["discord","validation","payload"],"backgroundTag":"invalid-identifier-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}