paperclipai/paperclip · error · Error

${prefix}: "timeoutPolicy" must be one of ${ADAPTER_LOGIN_TI

Error message

${prefix}: "timeoutPolicy" must be one of ${ADAPTER_LOGIN_TIMEOUT_POLICIES.join(", ")}.

What it means

Thrown by assertValidAdapterLoginCapability when loginCapability.timeoutPolicy is not "caller_bounded" or "fixed". The policy decides who owns the login timeout: caller_bounded lets the caller pass a deadline, fixed binds the login to one adapter-defined timeout.

Source

Thrown at packages/adapter-utils/src/login-capability.ts:131

): asserts value is AdapterLoginCapability {
  const prefix = `Adapter "${adapterType}" declares an invalid login capability`;
  if (typeof value !== "object" || value === null) {
    throw new Error(`${prefix}: the capability must be an object.`);
  }
  const cap = value as Record<string, unknown>;

  if (!isOneOf(ADAPTER_LOGIN_PANEL_MODES, cap.panelMode)) {
    throw new Error(
      `${prefix}: "panelMode" must be one of ${ADAPTER_LOGIN_PANEL_MODES.join(", ")}.`,
    );
  }
  if (!isOneOf(ADAPTER_LOGIN_SANDBOX_TRANSPORTS, cap.sandboxTransport)) {
    throw new Error(
      `${prefix}: "sandboxTransport" must be one of ${ADAPTER_LOGIN_SANDBOX_TRANSPORTS.join(", ")}.`,
    );
  }
  if (!isOneOf(ADAPTER_LOGIN_TIMEOUT_POLICIES, cap.timeoutPolicy)) {
    throw new Error(
      `${prefix}: "timeoutPolicy" must be one of ${ADAPTER_LOGIN_TIMEOUT_POLICIES.join(", ")}.`,
    );
  }
  if (typeof cap.getCommand !== "function") {
    throw new Error(`${prefix}: "getCommand" must be a function.`);
  }
  if (typeof cap.parsePrompt !== "function") {
    throw new Error(`${prefix}: "parsePrompt" must be a function.`);
  }
  if (cap.captureCredential !== undefined && typeof cap.captureCredential !== "function") {
    throw new Error(`${prefix}: "captureCredential" must be a function when present.`);
  }
  if (cap.onComplete !== undefined && typeof cap.onComplete !== "function") {
    throw new Error(`${prefix}: "onComplete" must be a function when present.`);
  }
  if (
    cap.completionClaim !== undefined &&
    !isOneOf(ADAPTER_LOGIN_COMPLETION_CLAIMS, cap.completionClaim)

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Set timeoutPolicy to "caller_bounded" (caller supplies the deadline) or "fixed" (the adapter's fixed timeout applies).
  2. Type the field as AdapterLoginTimeoutPolicy for compile-time enforcement.
  3. Run validateAdapterLoginCapability(module) in the adapter's test suite.
  4. Check adapter-utils version alignment if the value looks valid but still fails.

Example fix

// before
loginCapability = { timeoutPolicy: "caller-bounded", ... };

// after
loginCapability = { timeoutPolicy: "caller_bounded" as const, ... };
Defensive patterns

Strategy: type-guard

Validate before calling

import { ADAPTER_LOGIN_TIMEOUT_POLICIES } from "@paperclipai/adapter-utils";

const ok = (ADAPTER_LOGIN_TIMEOUT_POLICIES as readonly string[]).includes(capability.timeoutPolicy);

Type guard

import { ADAPTER_LOGIN_TIMEOUT_POLICIES, type AdapterLoginCapability } from "@paperclipai/adapter-utils";

function hasValidTimeoutPolicy(v: unknown): v is AdapterLoginCapability {
  const c = v as Record<string, unknown>;
  return (ADAPTER_LOGIN_TIMEOUT_POLICIES as readonly string[]).includes(c?.timeoutPolicy as string);
}

Try / catch

try {
  validateAdapterLoginCapability(mod);
} catch (error) {
  throw new Error(`adapter ${mod.type} cannot be registered: ${String(error)}`);
}

Prevention

When it happens

Trigger: An adapter exports loginCapability with timeoutPolicy missing, spelled differently ("caller-bounded", "adaptive"), or set to null; validateAdapterLoginCapability runs at load time and rejects the module.

Common situations: Copy-pasting a capability template and leaving placeholders; kebab-case vs snake_case confusion; constructing capabilities from user/config input where the string is never type-checked.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21). Data as JSON: /api/errors/91f3227bd8b157e5. Report an issue: GitHub.