paperclipai/paperclip · error · Error

/ : tool api_key requires keyPlacement

Error message

${app.slug}/${connectionMethod.key}: tool api_key requires keyPlacement

What it means

validateApp() requires that every connection method using api_key auth and a 'tool' purpose (the default) declares keyPlacement — where the API key is placed in requests (e.g. header/query). Without it, generated connection code cannot construct authenticated requests, so the definition is rejected.

Solutions

  1. Set keyPlacement on the api_key connection method (e.g. keyPlacement: { in: "header", name: "Authorization", format: "Bearer {apiKey}" }).
  2. If the key is not used for tool-call auth but for channel delivery, set purpose: "channel" to opt out of the requirement.
  3. Check the method() helper call for the app and pass keyPlacement through the extra object.

Example fix

// before
method("acme-key", "http", "api_key", defaults, "medium", guide)
// after
method("acme-key", "http", "api_key", defaults, "medium", guide, { keyPlacement: { in: "header", name: "X-Api-Key" } })
Defensive patterns

Strategy: validation

Validate before calling

for (const m of app.methods) {
  if (m.auth === 'api_key' && !m.keyPlacement && (m.purpose ?? 'tool') === 'tool') {
    throw new Error(`${app.slug}/${m.key}: set keyPlacement or purpose 'channel'`);
  }
}

Type guard

const hasKeyPlacement = (m) => m.auth !== 'api_key' || m.purpose === 'channel' || Boolean(m.keyPlacement);

Prevention

When it happens

Trigger: For a method with auth === "api_key" and keyPlacement undefined/null, when purpose is missing (defaults to "tool") or explicitly "tool". Channel-purpose methods (purpose === "channel") are exempt.

Common situations: Adding a new API-key app and forgetting to pass keyPlacement in the method's extra options; copying an oauth method block and switching auth to "api_key" without adding the placement field; a template regression that drops keyPlacement for newly ingested providers.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/c7bdc2458a085331. Report an issue: GitHub.

Appendix: source

Thrown at scripts/ingest-app-definitions.mjs:1586

 // AI account flow; saved REST connections remain removable through Connections.
 app.methods = [...methods, ...app.methods.filter(method => method.transport !== "rest_api")];
}
const validateApp = (app) => {
  if (
    app.schemaVersion !== 1 ||
    !app.slug ||
    !app.name ||
    !Array.isArray(app.methods) ||
    app.methods.length === 0
  )
    throw new Error(`${app.slug || "unknown"}: invalid AppDefinition`);
  for (const connectionMethod of app.methods) {
    if (
      connectionMethod.auth === "api_key" &&
      !connectionMethod.keyPlacement &&
      (connectionMethod.purpose ?? "tool") !== "channel"
    )
      throw new Error(
        `${app.slug}/${connectionMethod.key}: tool api_key requires keyPlacement`,
      );
    if (
      connectionMethod.auth === "oauth" &&
      connectionMethod.ownershipModes.length === 0
    )
      throw new Error(
        `${app.slug}/${connectionMethod.key}: oauth requires ownershipModes`,
      );
    for (const connectionField of [
      ...(connectionMethod.tenantFields ?? []),
      ...(connectionMethod.extensionFields ?? []),
      ...(connectionMethod.credentialFields ?? []),
    ])
      if (
        connectionField.required &&
        connectionField.type !== "checkbox" &&
        !connectionField.placeholder

View on GitHub (pinned to 3f1d897a7c)