paperclipai/paperclip · error · Error
${prefix}: "onComplete" must be a function when present.
Error message
${prefix}: "onComplete" must be a function when present. What it means
Thrown by assertValidAdapterLoginCapability when the OPTIONAL member onComplete is present but not a function. onComplete(ctx: AdapterLoginCompletionContext) => Promise<void> runs after a successful login to record non-secret completion state (e.g. the stored session id); absent is allowed, present-but-not-a-function means a malformed capability.
Source
Thrown at packages/adapter-utils/src/login-capability.ts:145
`${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)
) {
throw new Error(
`${prefix}: "completionClaim" must be one of ${ADAPTER_LOGIN_COMPLETION_CLAIMS.join(", ")} when present.`,
);
}
}
/**
* Validates the optional login capability of an adapter module. The function is
* a no-op when the module declares no login capability. It throws a clear error
* when the module declares a malformed capability, so the loader fails closed.
*/
export function validateAdapterLoginCapability(mod: {
type?: unknown;View on GitHub (pinned to a7e689b3c3)
Solutions
- Drop onComplete if the adapter needs no post-login bookkeeping.
- Or provide an async function receiving AdapterLoginCompletionContext and recording the non-secret state; it must carry no credential byte.
- If you meant to declare that a session id was stored, use completionClaim: "storedSessionId" instead.
- Type the capability as AdapterLoginCapability so non-function values fail to compile.
Example fix
// before
loginCapability = { ..., onComplete: { storedSessionId: true } };
// after
loginCapability = {
...,
onComplete: async (ctx) => { if (ctx.storedSessionId) await persistSessionRef(ctx.storedSessionId); },
completionClaim: "storedSessionId",
}; Defensive patterns
Strategy: type-guard
Validate before calling
const cap = capability as { onComplete?: unknown };
const ok = cap.onComplete === undefined || typeof cap.onComplete === "function"; Type guard
import type { AdapterLoginCapability } from "@paperclipai/adapter-utils";
function onCompleteOk(v: unknown): v is AdapterLoginCapability {
const c = v as { onComplete?: unknown };
return c.onComplete === undefined || typeof c.onComplete === "function";
} Try / catch
try {
assertValidAdapterLoginCapability(cap, adapterType);
} catch (error) {
throw new Error(`optional member onComplete malformed: ${String(error)}`);
} Prevention
- onComplete is the async hook; completionClaim is the string — do not swap them.
- Keep the hook non-secret: record ids, never credentials.
- Omit it entirely when no post-login bookkeeping is needed.
When it happens
Trigger: An adapter declares onComplete: undefined-adjacent junk such as a string description, a boolean, or a plain object of handlers; validateAdapterLoginCapability rejects the module at load.
Common situations: Declaring onComplete as metadata ({ storedSessionId: true }) instead of the hook function; converting a capability to JSON and back; mixing up completionClaim (a string) and onComplete (the hook).
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- ${prefix}: "panelMode" must be one of ${ADAPTER_LOGIN_PANEL_
- ${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.
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/afe203d0efe824d4.
Report an issue: GitHub.