paperclipai/paperclip · error · Error

Invalid Discord form token

Error message

Invalid Discord form token

What it means

correctionId() in chat-discord-question-forms.ts derives the storage key for a question-form correction retry from a submitActionId that must be a high-entropy token of the exact form 'pcfs:' + 22 chars of [A-Za-z0-9_-]. Anything else throws 'Invalid Discord form token', refusing to build a correction id from an untrusted identifier.

Source

Thrown at server/src/services/chat-discord-question-forms.ts:58

}

const token = (value: unknown, prefix: string, length = 22): value is string =>
  typeof value === "string" &&
  new RegExp(`^${prefix}[A-Za-z0-9_-]{${length}}$`).test(value);

export function isDiscordQuestionFormCorrectionId(
  value: unknown,
): value is string {
  return token(value, "pcfr:", 43);
}

function correctionId(
  scope: ChatSdkStateScope,
  owner: DiscordQuestionFormDraftOwner,
  submitActionId: string,
) {
  if (!token(submitActionId, "pcfs:"))
    throw new Error("Invalid Discord form token");
  // Includes the high-entropy secret submit token, not merely public IDs. One
  // overwritable row per form+actor bounds retry storage without persisting a
  // Discord interaction token or raw callback envelope.
  return `pcfr:${createHash("sha256")
    .update(
      JSON.stringify([
        scope.companyId,
        scope.endpointId,
        submitActionId,
        owner.principalId,
        owner.userId,
        owner.externalUserId,
      ]),
    )
    .digest("base64url")}`;
}

function validDraft(

View on GitHub (pinned to 01ad858492)

Solutions

  1. Verify the submitActionId was produced by the form-issuing path (isDiscordQuestionForm-style token check) before calling loadDiscordQuestionFormCorrection.
  2. Fix custom-id parsing so the 'pcfs:...' token is extracted intact (no URL-decoding/trimming/splitting damage).
  3. Reject bad tokens at the interaction handler entry and re-issue a fresh form instead of attempting correction lookup.
  4. Log the received token shape (prefix/length only) to identify where corruption happens.

Example fix

// before
const submitActionId = customId.split(":").pop(); // may mangle token
// after
if (!isDiscordQuestionFormSubmitActionId(customId)) return respondInvalid();
const submitActionId = customId.slice("pcqf:".length); // exact token extraction
Defensive patterns

Strategy: validation

Validate before calling

const isSubmitActionToken = (v: unknown): v is string =>
  typeof v === "string" && /^pcfs:[A-Za-z0-9_-]{22}$/.test(v);
if (!isSubmitActionToken(submitActionId)) throw new Error("malformed pcfs token before correction lookup");

Type guard

const isDiscordFormSubmitToken = (v: unknown): v is string =>
  typeof v === "string" && /^pcfs:[A-Za-z0-9_-]{22}$/.test(v);

Try / catch

try { const correction = await loadDiscordQuestionFormCorrection(scope, owner, submitActionId); }
catch (e) {
  if ((e as Error).message === "Invalid Discord form token") { return respondWithFreshForm(); }
  throw e;
}

Prevention

When it happens

Trigger: Calling correctionId (via actionId, key, or loadDiscordQuestionFormCorrection) with a submitActionId that is not a string, lacks the 'pcfs:' prefix, has the wrong length (≠22 chars after the prefix), or contains characters outside [A-Za-z0-9_-] — e.g. a raw Discord interaction/custom-id fragment or a truncated/partially-decoded action id.

Common situations: Parsing a Discord component custom_id with the wrong split so the tail still contains separators; passing a legacy/other-format action id; a client submitting a forged or truncated token; storing/round-tripping the action id through something that altered its length or encoding (URL-encoding, trimming).

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/0cd40567a00a008f. Report an issue: GitHub.