{"record":{"id":"0a3cd8a71d1963f9","repo":"paperclipai/paperclip","slug":"prefix-panelmode-must-be-one-of-adapter-lo","errorCode":null,"errorMessage":"${prefix}: \"panelMode\" must be one of ${ADAPTER_LOGIN_PANEL_MODES.join(\", \")}.","messagePattern":"(.+?): \"panelMode\" must be one of (.+?)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/login-capability.ts","lineNumber":121,"sourceCode":"/**\n * Validates one login capability. The function fails closed: it throws a clear\n * error for a malformed shape. It checks each scalar field against its fixed\n * value set, checks each required function member, and checks each optional\n * member only when the member is present. `adapterType` names the adapter in the\n * error text.\n */\nexport function assertValidAdapterLoginCapability(\n  value: unknown,\n  adapterType: string,\n): asserts value is AdapterLoginCapability {\n  const prefix = `Adapter \"${adapterType}\" declares an invalid login capability`;\n  if (typeof value !== \"object\" || value === null) {\n    throw new Error(`${prefix}: the capability must be an object.`);\n  }\n  const cap = value as Record<string, unknown>;\n\n  if (!isOneOf(ADAPTER_LOGIN_PANEL_MODES, cap.panelMode)) {\n    throw new Error(\n      `${prefix}: \"panelMode\" must be one of ${ADAPTER_LOGIN_PANEL_MODES.join(\", \")}.`,\n    );\n  }\n  if (!isOneOf(ADAPTER_LOGIN_SANDBOX_TRANSPORTS, cap.sandboxTransport)) {\n    throw new Error(\n      `${prefix}: \"sandboxTransport\" must be one of ${ADAPTER_LOGIN_SANDBOX_TRANSPORTS.join(\", \")}.`,\n    );\n  }\n  if (!isOneOf(ADAPTER_LOGIN_TIMEOUT_POLICIES, cap.timeoutPolicy)) {\n    throw new Error(\n      `${prefix}: \"timeoutPolicy\" must be one of ${ADAPTER_LOGIN_TIMEOUT_POLICIES.join(\", \")}.`,\n    );\n  }\n  if (typeof cap.getCommand !== \"function\") {\n    throw new Error(`${prefix}: \"getCommand\" must be a function.`);\n  }\n  if (typeof cap.parsePrompt !== \"function\") {\n    throw new Error(`${prefix}: \"parsePrompt\" must be a function.`);","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/packages/adapter-utils/src/login-capability.ts#L103-L139","documentation":"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.","triggerScenarios":"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.'","commonSituations":"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.","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)."],"exampleFix":"// before\nexport const loginCapability = {\n  panelMode: \"code_display\",\n  ...\n};\n\n// after\nimport { type AdapterLoginPanelMode } from \"@paperclipai/adapter-utils\";\n\nexport const loginCapability = {\n  panelMode: \"displayed_code\" as AdapterLoginPanelMode,\n  ...\n};","handlingStrategy":"type-guard","validationCode":"import { ADAPTER_LOGIN_PANEL_MODES } from \"@paperclipai/adapter-utils\";\n\nconst ok = (ADAPTER_LOGIN_PANEL_MODES as readonly string[]).includes(capability.panelMode);","typeGuard":"import { ADAPTER_LOGIN_PANEL_MODES, type AdapterLoginCapability } from \"@paperclipai/adapter-utils\";\n\nfunction isValidLoginCapability(v: unknown): v is AdapterLoginCapability {\n  if (typeof v !== \"object\" || v === null) return false;\n  const c = v as Record<string, unknown>;\n  return (ADAPTER_LOGIN_PANEL_MODES as readonly string[]).includes(c.panelMode as string);\n}","tryCatchPattern":"try {\n  validateAdapterLoginCapability(adapterModule);\n} catch (error) {\n  // fail the adapter load with the validator's message; do not register a partial capability\n  throw new Error(`refusing to load adapter: ${String(error)}`);\n}","preventionTips":["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."],"tags":["adapter","login-capability","validation","typescript"],"backgroundTag":"invalid-enum-value","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-21T17:58:32.592Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}