paperclipai/paperclip · error · Error

${prefix}: "panelMode" must be one of ${ADAPTER_LOGIN_PANEL_

Error message

${prefix}: "panelMode" must be one of ${ADAPTER_LOGIN_PANEL_MODES.join(", ")}.

What it means

Thrown by assertValidAdapterLoginCapability in @paperclipai/adapter-utils when an adapter module declares a loginCapability whose panelMode is not one of the fixed values "displayed_code" or "submitted_browser_code". The validator fails closed so the adapter loader never accepts a partial capability: panelMode decides whether the server's login panel shows a one-time code the user types into the browser, or accepts a code pasted back from the browser.

Source

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

/**
 * Validates one login capability. The function fails closed: it throws a clear
 * error for a malformed shape. It checks each scalar field against its fixed
 * value set, checks each required function member, and checks each optional
 * member only when the member is present. `adapterType` names the adapter in the
 * error text.
 */
export function assertValidAdapterLoginCapability(
  value: unknown,
  adapterType: string,
): 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.`);

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Set panelMode to "displayed_code" if the login command prints a code the user enters at the vendor's website, or "submitted_browser_code" if the user must paste a code from the browser back into the panel.
  2. Type the field as AdapterLoginPanelMode (import from @paperclipai/adapter-utils) so the compiler rejects invalid literals before runtime.
  3. Add a unit test that calls validateAdapterLoginCapability on the exported module so CI catches a malformed capability.
  4. If the value looks correct, check for a version skew between the adapter package and @paperclipai/adapter-utils (the exported ADAPTER_LOGIN_PANEL_MODES array is the source of truth).

Example fix

// before
export const loginCapability = {
  panelMode: "code_display",
  ...
};

// after
import { type AdapterLoginPanelMode } from "@paperclipai/adapter-utils";

export const loginCapability = {
  panelMode: "displayed_code" as AdapterLoginPanelMode,
  ...
};
Defensive patterns

Strategy: type-guard

Validate before calling

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

const ok = (ADAPTER_LOGIN_PANEL_MODES as readonly string[]).includes(capability.panelMode);

Type guard

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

function isValidLoginCapability(v: unknown): v is AdapterLoginCapability {
  if (typeof v !== "object" || v === null) return false;
  const c = v as Record<string, unknown>;
  return (ADAPTER_LOGIN_PANEL_MODES as readonly string[]).includes(c.panelMode as string);
}

Try / catch

try {
  validateAdapterLoginCapability(adapterModule);
} catch (error) {
  // fail the adapter load with the validator's message; do not register a partial capability
  throw new Error(`refusing to load adapter: ${String(error)}`);
}

Prevention

When it happens

Trigger: An adapter package exports loginCapability = { panelMode: "show_code", ... } (typo or invented value) or omits panelMode entirely. validateAdapterLoginCapability(mod) / assertValidAdapterLoginCapability(value, adapterType) runs at adapter load/registration time and rejects the module with 'Adapter "<type>" declares an invalid login capability: "panelMode" must be one of displayed_code, submitted_browser_code.'

Common situations: Renaming or adding panel modes in a newer adapter-utils version while the adapter still uses the old vocabulary; copying a capability object from another adapter and hand-editing fields; constructing the capability from parsed JSON/config data so TypeScript never checks the literal.

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/0a3cd8a71d1963f9. Report an issue: GitHub.