paperclipai/paperclip · error · Error

OAuth callback connection belongs to a missing company

Error message

OAuth callback connection belongs to a missing company

What it means

oauthAppPath builds a callback redirect path of the form /:issuePrefix/apps/:connectionId/permissions and requires the company row to exist to read its issuePrefix. When the companyId referenced by the OAuth connection has no matching row in the companies table, it throws this Error, which surfaces as a 500 on the OAuth callback/permissions routes.

Source

Thrown at server/src/routes/tool-access.ts:445

    if (!host) return null;
    try {
      const parsed = new URL(`${req.protocol}://${host}`);
      return isLoopbackHost(parsed.hostname) ? parsed.origin : null;
    } catch {
      return null;
    }
  }

  async function oauthAppPath(
    companyId: string,
    connectionId: string,
  ) {
    const [company] = await db
      .select({ issuePrefix: companies.issuePrefix })
      .from(companies)
      .where(eq(companies.id, companyId))
      .limit(1);
    if (!company) throw new Error("OAuth callback connection belongs to a missing company");
    return `/${company.issuePrefix}/apps/${connectionId}/permissions`;
  }

function connectorEnrollmentPrincipal(req: Request): string {
  return req.actor.userId ? `user:${req.actor.userId}` : `source:${req.actor.source ?? "board"}`;
}

/**
   * A failed first authorization is still an incomplete setup, not an app
   * configuration task. Send it back to the same exact draft so the operator
   * can retry the missing checkpoint. Reauthorization of an already-active
   * connection keeps the established detail-page recovery route.
   */
  async function oauthRecoveryPath(
    connection: ToolConnection,
    outcome: "failed" | "denied",
    code?: string | null,
    providerRecovery?: { installationUrl?: unknown; managementUrl?: unknown },

View on GitHub (pinned to 01ad858492)

Solutions

  1. Delete or reassign the orphaned OAuth connection so its companyId references a live company row.
  2. Restore/re-insert the missing companies row (with correct issuePrefix) that the connection references.
  3. Add an ON DELETE cascade or cleanup job for connections when companies are removed to prevent orphans.
  4. Harden oauthAppPath to return a 404/redirect instead of throwing for missing companies.

Example fix

// before
if (!company) throw new Error("OAuth callback connection belongs to a missing company");
// after
if (!company) return null; // caller redirects to an app-error page instead of a 500
Defensive patterns

Strategy: try-catch

Validate before calling

const [company] = await db.select().from(companies).where(eq(companies.id, connection.companyId));
if (!company) throw new Error(`connection ${connection.id} references deleted company ${connection.companyId}`);

Type guard

function hasLiveCompany(connection, company) { return Boolean(company && company.id === connection.companyId && company.issuePrefix); }

Try / catch

try {
  return buildOauthAppPath(connection.companyId, connection.id);
} catch (e) {
  logger.error({ connectionId: connection.id, companyId: connection.companyId }, e.message);
  return null; // render generic OAuth error page
}

Prevention

When it happens

Trigger: OAuth callback or permissions resolution (via detailPermissionsPath/permissionsPath) for a connection whose companies FK points at a deleted or never-created company — the SELECT on companies by id returns empty and the function throws.

Common situations: Company hard-deleted while OAuth connections remained (missing cascade); cross-environment DB restore where companies rows were dropped; seeded connection rows with bogus companyIds; environments synced partially.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/95fbb4125bc8427f. Report an issue: GitHub.