paperclipai/paperclip · error · Error

Invalid Discord command registration authority

Error message

Invalid Discord command registration authority

What it means

reconcileDiscordCommandRegistration validates its preconditions before doing any I/O: the persisted state must parse, the runtime fence schema must pass, the verified bot identity must match the scope's application/guild IDs, and the bot token must exist, be ≤4096 chars, and contain no CR/LF. Any mismatch throws 'Invalid Discord command registration authority'.

Source

Thrown at server/src/services/chat-discord-command-registration.ts:426

 * reconciliation identity; the marker alone never grants command ownership.
 * Unknown writes remain attempted until GET shows the exact expected command.
 * An absent command after timeout is NOT proof that the POST never committed.
 */
export async function reconcileDiscordCommandRegistration(
  input: ReconcileDiscordCommandRegistrationOptions,
): Promise<DiscordCommandRegistrationResult> {
  let state = parseDiscordCommandRegistration(input.state, input.scope, true);
  const runtimeFence = fenceSchema.safeParse(input.runtimeFence);
  if (
    !state ||
    !runtimeFence.success ||
    input.verifiedIdentity.botExternalId !== input.scope.applicationId ||
    input.verifiedIdentity.providerAccountId !== input.scope.guildId ||
    !input.botToken ||
    input.botToken.length > 4096 ||
    /[\r\n]/.test(input.botToken)
  ) {
    throw new Error("Invalid Discord command registration authority");
  }
  // The caller may retain its options while an authorization hook is held.
  // Snapshot the validated identity, credential and HTTP function before await.
  input = {
    ...input,
    scope: state.scope,
    state,
    runtimeFence: Object.freeze(runtimeFence.data),
    verifiedIdentity: Object.freeze({ ...input.verifiedIdentity }),
  };
  const authorize = async (stage: DiscordCommandRegistrationStage) => {
    try {
      await input.authorize(stage);
    } catch {
      throw new Error("Discord command registration authorization denied");
    }
  };
  const persist = async (next: DiscordCommandRegistration) => {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Trim the bot token of whitespace/newlines before storing (token may be ≤4096 chars, no CR/LF).
  2. Ensure verifiedIdentity comes from a verification run against the same applicationId and guildId as scope.
  3. Re-run identity verification and reconciliation as a unit so state, scope, and identity stay consistent.
  4. Do not reuse registration state blobs across scopes; regenerate state for the target application/guild.

Example fix

// before
const token = process.env.DISCORD_BOT_TOKEN; // may contain \n
// after
const token = process.env.DISCORD_BOT_TOKEN?.trim();
Defensive patterns

Strategy: validation

Validate before calling

const ok =
  state &&
  verifiedIdentity.botExternalId === scope.applicationId &&
  verifiedIdentity.providerAccountId === scope.guildId &&
  botToken && botToken.length <= 4096 && !/[\r\n]/.test(botToken);
if (!ok) throw new Error("registration authority mismatch before calling reconcile");

Type guard

const validAuthority = (i: ReconcileDiscordCommandRegistrationOptions) =>
  Boolean(i.botToken) && i.botToken.length <= 4096 && !/[\r\n]/.test(i.botToken) &&
  i.verifiedIdentity.botExternalId === i.scope.applicationId &&
  i.verifiedIdentity.providerAccountId === i.scope.guildId;

Try / catch

try { await reconcileDiscordCommandRegistration(input); }
catch (e) { if ((e as Error).message.includes("Invalid Discord command registration authority")) { await reverifyIdentityAndReseedState(); return retry(); } throw e; }

Prevention

When it happens

Trigger: Calling reconcile with a state blob from a different application/guild scope; verifiedIdentity.botExternalId not equal to scope.applicationId or providerAccountId not equal to scope.guildId; missing/empty bot token; token containing newlines (often from copy-paste or env files); malformed state or runtimeFence that fails fenceSchema.

Common situations: Bot token pasted with a trailing newline from a .env file; swapping Discord applications (new client id) without re-registering; state rows copied between companies/endpoints; identity verification run against a different guild than the registration scope.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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