paperclipai/paperclip · warning · SetupTokenSessionError

Too many active setup-token login sessions.

Error message

Too many active setup-token login sessions.

What it means

Capacity rejection from SetupTokenSessionService.start: the per-slot (per-owner) cap is enforced synchronously in reserveCapacity — a slot is the company + owner + adapter triple, and when reservedBySlot for that slot already equals caps.perOwner, the start fails closed with SetupTokenSessionError 429 SETUP_TOKEN_CAP_EXCEEDED ("Too many active setup-token login sessions.", server/src/services/setup-token-session.ts:799). The reservation happens before any await, so concurrent starts cannot both squeeze through; a rejected start increments nothing.

Source

Thrown at server/src/services/setup-token-session.ts:836

  private static decrementCount(counts: Map<string, number>, key: string): void {
    const next = (counts.get(key) ?? 0) - 1;
    if (next > 0) {
      counts.set(key, next);
    } else {
      counts.delete(key);
    }
  }

  /** Builds the company, owner, and adapter slot key. The reservation and the
   *  database active-slot unique index share this identity. */
  private static slotKey(
    scope: Pick<SetupTokenSessionScope, "companyId" | "ownerUserId" | "adapterType">,
  ): string {
    return [scope.companyId, scope.ownerUserId, scope.adapterType].join("\u0000");
  }

  /**
   * Reserves the capacity for one start under every enforced cap. The method is
   * synchronous, so it runs to completion before the first `await` in
   * {@link start}. Two concurrent starts for one slot cannot interleave inside
   * it: the first reserves the slot, and the second reads the incremented count
   * and fails closed with the fixed 429 cap error. The method holds the per-slot
   * and per-company semantics; the slot is the company, the owner, and the
   * adapter. It increments no counter on a rejection, so a rejected start
   * reserves nothing.
   */
  private reserveCapacity(scope: SetupTokenSessionScope): CapReservation {
    const slotKey = SetupTokenSessionService.slotKey(scope);
    if ((this.reservedBySlot.get(slotKey) ?? 0) >= this.caps.perOwner) {
      throw new SetupTokenSessionError(429, SETUP_TOKEN_CAP_EXCEEDED);
    }
    if ((this.reservedByCompany.get(scope.companyId) ?? 0) >= this.caps.perCompany) {
      throw new SetupTokenSessionError(429, SETUP_TOKEN_CAP_EXCEEDED);
    }
    SetupTokenSessionService.incrementCount(this.reservedBySlot, slotKey);

View on GitHub (pinned to 01ad858492)

Solutions

  1. List the owner's active setup-token sessions and cancel/expire the stale ones (abort or wait for TTL) before starting a new one.
  2. Fix client retry loops to reuse the existing session (same sessionId) instead of starting a new login on each attempt.
  3. Wait for the TTL to reap lingering sessions if no cancel API is available.
  4. If the workload legitimately needs more concurrent logins per owner, raise caps.perOwner in service configuration.
Defensive patterns

Strategy: retry

Validate before calling

const active = await setupTokenSessions.listActive({ companyId, ownerUserId, adapterType });
if (active.length >= PER_OWNER_CAP) { await setupTokenSessions.cancel(active[0].sessionId); } // free one slot before start

Try / catch

try { return await svc.start(scope); } catch (err) { if (err instanceof SetupTokenSessionError && err.status === 429) { await cancelOldestActiveSession(scope); return await svc.start(scope); } throw err; }

Prevention

When it happens

Trigger: Calling start(scope) for a company/owner/adapter combination that already has caps.perOwner active setup-token login sessions — e.g. the same user starts repeated OAuth setup-token logins for the same adapter without letting earlier sessions expire (TTL) or reach a terminal state.

Common situations: Retry loops hammering the login-start endpoint after a slow adapter; multiple browser tabs each starting a login; tests that start sessions without terminating them; sessions lingering because the login process never completes and the TTL is long.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-21). Data as JSON: /api/errors/e50113a6bc558faa. Report an issue: GitHub.