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
- 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.
- Type the field as AdapterLoginPanelMode (import from @paperclipai/adapter-utils) so the compiler rejects invalid literals before runtime.
- Add a unit test that calls validateAdapterLoginCapability on the exported module so CI catches a malformed capability.
- 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
- Type every capability object as AdapterLoginCapability so invalid panelMode literals fail at compile time.
- Call validateAdapterLoginCapability in each adapter's unit test and in the loader.
- Never assemble loginCapability from untyped JSON/config without an explicit type assertion through a guard.
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
- ${prefix}: "sandboxTransport" must be one of ${ADAPTER_LOGIN
- ${prefix}: "timeoutPolicy" must be one of ${ADAPTER_LOGIN_TI
- ${prefix}: "getCommand" must be a function.
- ${prefix}: "parsePrompt" must be a function.
- ${prefix}: "captureCredential" must be a function when prese
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/0a3cd8a71d1963f9.
Report an issue: GitHub.