affaan-m/ECC · error · Error
invalid interaction id/token
Error message
invalid interaction id/token
What it means
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.
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.
Example fix
// before
const url = interactionCallbackUrl({ id: payload.d.interaction_id, token: payload.d.interaction_token });
// after (use the nested interaction object as delivered)
const interaction = payload.d.interaction ?? payload.d;
const url = interactionCallbackUrl(interaction); Defensive patterns
Strategy: type-guard
Validate before calling
const SNOWFLAKE_RE = /^\d{17,20}$/;
const TOKEN_RE = /^[A-Za-z0-9._-]{1,255}$/;
function canBuildCallbackUrl(interaction) {
return SNOWFLAKE_RE.test(String(interaction?.id ?? '')) && TOKEN_RE.test(String(interaction?.token ?? ''));
}
if (!canBuildCallbackUrl(interaction)) return; // skip/drop corrupt interaction Type guard
const hasValidInteraction = (i) =>
typeof i === 'object' && i !== null &&
/^\d+$/.test(String(i.id ?? '')) &&
typeof i.token === 'string' && /^[A-Za-z0-9._-]{1,255}$/.test(i.token); Try / catch
try {
await fetch(interactionCallbackUrl(interaction), init);
} catch (err) {
if (err.message === 'invalid interaction id/token') {
console.warn(`Dropping interaction ${interaction?.id ?? '(unknown id)'}: malformed id/token`);
return;
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- capsule.payload_denied
- capsule.payload_invalid
- invalid Discord webhook URL
- -32602
- a claim token is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/3afd2d6199c09503.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/discord/ecc-bot.mjs:45
const API = 'https://discord.com/api/v10';
// Strip CR/LF from string args so Discord-payload-controlled values (command
// names, usernames) cannot forge or inject extra log lines (log injection).
const log = (...a) =>
console.log(new Date().toISOString(), ...a.map(x => (typeof x === 'string' ? x.replace(/[\r\n]+/g, ' ') : x)));
// Interaction ids are Discord snowflakes (numeric) and tokens are a bounded
// URL-safe set. Validate before building the callback URL so a malformed or
// hostile gateway payload cannot inject path segments / alter the request
// target (SSRF). The host is always the fixed API constant.
const SNOWFLAKE_RE = /^[0-9]{1,20}$/;
const INTERACTION_TOKEN_RE = /^[A-Za-z0-9._-]{1,255}$/;
function interactionCallbackUrl(interaction) {
const id = String(interaction?.id ?? '');
const token = String(interaction?.token ?? '');
if (!SNOWFLAKE_RE.test(id) || !INTERACTION_TOKEN_RE.test(token)) {
throw new Error('invalid interaction id/token');
}
return `${API}/interactions/${id}/${token}/callback`;
}
// Clamp a remote-supplied timer interval to a sane range so a hostile/bogus
// heartbeat_interval cannot spin a tight loop or hang the bot (resource
// exhaustion). Discord's real value is ~41250ms.
function clampHeartbeatInterval(value) {
const n = Number(value);
if (!Number.isFinite(n)) return 41250;
return Math.max(1000, Math.min(n, 600000));
}
// ---------- skill + docs lookup (local clone as the data source) ----------
function parseFrontmatter(text) {
const m = text.match(/^---\n([\s\S]*?)\n---/);
if (!m) return {};View on GitHub (pinned to 8321021c54)