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
- Set timeoutPolicy to "caller_bounded" (caller supplies the deadline) or "fixed" (the adapter's fixed timeout applies).
- Type the field as AdapterLoginTimeoutPolicy for compile-time enforcement.
- Run validateAdapterLoginCapability(module) in the adapter's test suite.
- 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
- Use the exact snake_case spellings caller_bounded / fixed.
- Type the field as AdapterLoginTimeoutPolicy.
- Keep capability fixtures in one place so all adapters share verified values.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ${prefix}: "panelMode" must be one of ${ADAPTER_LOGIN_PANEL_
- ${prefix}: "sandboxTransport" must be one of ${ADAPTER_LOGIN
- ${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/91f3227bd8b157e5.
Report an issue: GitHub.