{"record":{"id":"a6db4a913e9afbee","repo":"paperclipai/paperclip","slug":"prefix-getcommand-must-be-a-function","errorCode":null,"errorMessage":"${prefix}: \"getCommand\" must be a function.","messagePattern":"(.+?): \"getCommand\" must be a function\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/login-capability.ts","lineNumber":136,"sourceCode":"  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.`);\n  }\n  if (cap.captureCredential !== undefined && typeof cap.captureCredential !== \"function\") {\n    throw new Error(`${prefix}: \"captureCredential\" must be a function when present.`);\n  }\n  if (cap.onComplete !== undefined && typeof cap.onComplete !== \"function\") {\n    throw new Error(`${prefix}: \"onComplete\" must be a function when present.`);\n  }\n  if (\n    cap.completionClaim !== undefined &&\n    !isOneOf(ADAPTER_LOGIN_COMPLETION_CLAIMS, cap.completionClaim)\n  ) {\n    throw new Error(\n      `${prefix}: \"completionClaim\" must be one of ${ADAPTER_LOGIN_COMPLETION_CLAIMS.join(\", \")} when present.`,\n    );\n  }","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/packages/adapter-utils/src/login-capability.ts#L118-L154","documentation":"Thrown by assertValidAdapterLoginCapability when loginCapability.getCommand is missing or not a function. getCommand is the required member that returns the fixed, non-secret login command string the server runs in the sandbox; the validator rejects non-function values so a static string command can never slip through.","triggerScenarios":"An adapter declares loginCapability = { getCommand: \"paperclip login\", ... } (the raw string instead of a function) or omits getCommand while declaring the rest of the capability; the loader's validateAdapterLoginCapability throws before registration completes.","commonSituations":"Authors storing the command directly as a string because it is constant; partial capability objects written for documentation; refactors that move command construction elsewhere and leave the field dangling.","solutions":["Wrap the command in a zero-argument function: getCommand: () => \"mycli login --no-browser\".","Keep the function free of captured secrets; it must return only the fixed command (credentials are captured later via captureCredential).","Type the capability object as AdapterLoginCapability so a missing getCommand is a compile error.","Add a load-time test calling validateAdapterLoginCapability on the exported module."],"exampleFix":"// before\nloginCapability = { ..., getCommand: \"claude login\" };\n\n// after\nloginCapability = { ..., getCommand: () => \"claude login\" };","handlingStrategy":"type-guard","validationCode":"const ok = typeof (capability as { getCommand?: unknown }).getCommand === \"function\";","typeGuard":"import type { AdapterLoginCapability } from \"@paperclipai/adapter-utils\";\n\nfunction hasGetCommand(v: unknown): v is AdapterLoginCapability {\n  return typeof (v as { getCommand?: unknown })?.getCommand === \"function\";\n}","tryCatchPattern":"try {\n  assertValidAdapterLoginCapability(cap, \"my-adapter\");\n} catch (error) {\n  throw new Error(`login capability is malformed: ${String(error)}`);\n}","preventionTips":["getCommand is always () => string, never a bare string.","Keep the returned command constant and non-secret; secrets are captured by captureCredential later.","Let TypeScript check the whole capability object against AdapterLoginCapability before export."],"tags":["adapter","login-capability","validation","typescript"],"backgroundTag":"schema-validation-failed","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-21T17:58:32.592Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}