paperclipai/paperclip · error · RequestFailure

invalid_response

invalid_response

Error message

invalid_response

What it means

request() in the Discord command-registration reconciler validates requestTimeoutMs before making the Discord API call; if it is not an integer between 1 and 25000 inclusive it throws a RequestFailure with code 'invalid_response'. The message is terse because the guard runs before any network I/O — it is a pre-flight configuration validation, not Discord's reply.

Source

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

function abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {
  return new Promise<T>((resolve, reject) => {
    const abort = () => reject(new RequestFailure("request_failed"));
    signal.addEventListener("abort", abort, { once: true });
    if (signal.aborted) abort();
    promise
      .then(resolve, reject)
      .finally(() => signal.removeEventListener("abort", abort));
  });
}

async function request(
  input: ReconcileDiscordCommandRegistrationOptions,
  method: "GET" | "POST" | "PATCH",
  commandId?: string,
): Promise<unknown> {
  const timeoutMs = input.requestTimeoutMs ?? 25_000;
  if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 25_000)
    throw new RequestFailure("invalid_response");
  const signal = AbortSignal.timeout(timeoutMs);
  let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
  try {
    const response = await abortable(
      input.fetch(
        `https://discord.com/api/v10/applications/${input.scope.applicationId}/commands${commandId ? `/${commandId}` : ""}`,
        {
          method,
          signal,
          redirect: "error",
          headers: {
            authorization: `Bot ${input.botToken}`,
            ...(method === "GET" ? {} : { "content-type": "application/json" }),
          },
          ...(method === "GET"
            ? {}
            : {
                body: JSON.stringify(

View on GitHub (pinned to 01ad858492)

Solutions

  1. Set input.requestTimeoutMs to an integer between 1 and 25000 (e.g. 25000 for the maximum).
  2. Omit requestTimeoutMs entirely to use the default of 25000.
  3. Check the units — pass milliseconds, not seconds, and parse env values with Number() guarding against NaN.
  4. Clamp the value on the caller side: Math.min(25000, Math.max(1, Math.floor(ms))).

Example fix

// before
reconcile({ ..., requestTimeoutMs: Number(process.env.TIMEOUT_S) }) // '30' -> 30000 -> RequestFailure invalid_response
// after
const ms = Math.min(25000, Math.max(1, Math.floor(Number(process.env.TIMEOUT_S) * 1000 || 25000)));
reconcile({ ..., requestTimeoutMs: ms });
Defensive patterns

Strategy: validation

Validate before calling

if (!Number.isInteger(requestTimeoutMs) || requestTimeoutMs < 1 || requestTimeoutMs > 25000) throw new Error('requestTimeoutMs must be an integer in [1, 25000] ms');

Type guard

const isValidTimeoutMs = (v) => Number.isInteger(v) && v >= 1 && v <= 25000;

Try / catch

try {
  return await reconcileDiscordCommands(input);
} catch (e) {
  if (e?.code === 'invalid_response') {
    return reconcileDiscordCommands({ ...input, requestTimeoutMs: 25000 }); // fall back to default
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the reconciler (reconcileDiscordCommandRegistration path) with input.requestTimeoutMs set to 0, a negative number, a non-integer (e.g. 1500.5), or greater than 25000 (e.g. 60000 for a slow network).

Common situations: Operator sets a timeout above the 25s ceiling in config; a duration in seconds (e.g. 30) passed where milliseconds are expected (0.03 would also fail the integer check... 30 fails as <1ms semantics confusion); NaN from an unparsed env var like Number('25s').

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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