{"record":{"id":"1840d7619ecc72ab","repo":"nexu-io/open-design","slug":"interest-capacity-exceeded","errorCode":"interest_capacity_exceeded","errorMessage":"interest_capacity_exceeded","messagePattern":"interest_capacity_exceeded","errorType":"exception","errorClass":"WorkspaceBillingInterestError","httpStatus":null,"severity":"warning","filePath":"apps/daemon/src/collab/workspace-billing-runtime.ts","lineNumber":276,"sourceCode":"    const generationText = input.clientGeneration.trim();\n    if (!clientId || !/^(?:0|[1-9]\\d*)$/.test(generationText)) {\n      throw new WorkspaceBillingInterestError('invalid_generation');\n    }\n    const generation = BigInt(generationText);\n    const current = this.clients.get(clientId);\n    if (current && generation < current.generation) {\n      throw new WorkspaceBillingInterestError(\n        'stale_generation',\n        current.generation.toString(),\n      );\n    }\n    const keys = new Map<string, WorkspaceBillingRuntimeKey>();\n    for (const interest of input.interests) {\n      const key = normalizeKey(interest);\n      keys.set(runtimeKey(key), key);\n    }\n    if (keys.size > this.maxInterestsPerClient) {\n      throw new WorkspaceBillingInterestError('interest_capacity_exceeded');\n    }\n    if (current && generation === current.generation) {\n      if (!sameStringSet(current.keys, new Set(keys.keys()))) {\n        throw new WorkspaceBillingInterestError(\n          'generation_payload_mismatch',\n          current.generation.toString(),\n        );\n      }\n      current.expiresAt = this.scheduler.now() + this.interestLeaseMs;\n      return this.interestLease(clientId, current);\n    }\n    if (keys.size === 0) {\n      if (current) {\n        this.clients.delete(clientId);\n        this.handleInterestMutation(current.keys, new Set());\n      }\n      return {\n        clientId,","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/collab/workspace-billing-runtime.ts#L258-L294","documentation":"Thrown by setClientInterests when a single client's deduplicated interest set exceeds maxInterestsPerClient (default 16). The check occurs after normalizeKey/dedup via the Map at line 270-273, so duplicate keys count once. This cap prevents one client from monopolizing the coordinator's per-key refresh budget.","triggerScenarios":"Calling setClientInterests with an interests array that, after key normalization and dedup, produces more than maxInterestsPerClient distinct {workspaceId, workspaceMemberId} pairs. The default cap is 16.","commonSituations":"A dashboard client tries to watch billing for all 30 workspaces it can see. A bug in client-side interest logic sends every workspace/member pair instead of the focused set. The client doesn't prune stale interests before adding new ones.","solutions":["Reduce the interests array to at most maxInterestsPerClient entries, prioritizing the currently focused workspaces.","Check the configured maxInterestsPerClient if you customized the coordinator options.","Implement client-side eviction: drop least-recently-used interests when approaching the cap.","If the cap is genuinely too low for your use case, raise maxInterestsPerClient in the coordinator constructor options."],"exampleFix":"// before — sending all visible workspaces\nconst interests = allWorkspaces.map(w => ({ workspaceId: w.id, workspaceMemberId: w.memberId }));\nruntime.setClientInterests({ clientId, clientGeneration, interests });\n\n// after — cap to the focused set\nconst MAX = 16;\nconst interests = focusedWorkspaces\n  .slice(0, MAX)\n  .map(w => ({ workspaceId: w.id, workspaceMemberId: w.memberId }));\nruntime.setClientInterests({ clientId, clientGeneration, interests });","handlingStrategy":"validation","validationCode":"const MAX_INTERESTS_PER_CLIENT = 16; // must match coordinator config\n\nfunction sanitizeInterests(\n  interests: WorkspaceBillingRuntimeKey[],\n  max: number = MAX_INTERESTS_PER_CLIENT,\n): WorkspaceBillingRuntimeKey[] {\n  // Dedup by workspaceId+workspaceMemberId\n  const seen = new Set<string>();\n  const deduped = interests.filter(k => {\n    const id = `${k.workspaceId}:${k.workspaceMemberId}`;\n    if (seen.has(id)) return false;\n    seen.add(id);\n    return true;\n  });\n  if (deduped.length > max) {\n    throw new Error(\n      `Interest set of ${deduped.length} exceeds per-client cap of ${max}. ` +\n      `Reduce to the most important workspaces.`\n    );\n  }\n  return deduped;\n}\n\n// Before calling setClientInterests:\nconst safeInterests = sanitizeInterests(interests, coordinator.maxInterestsPerClient);","typeGuard":null,"tryCatchPattern":"try {\n  await coordinator.setClientInterests({ clientId, clientGeneration: String(gen), interests });\n} catch (error) {\n  if (error instanceof WorkspaceBillingInterestError &&\n      error.code === 'interest_capacity_exceeded') {\n    // Trim to the cap, prioritizing focused workspaces\n    const trimmed = interests.slice(0, MAX_INTERESTS_PER_CLIENT);\n    await coordinator.setClientInterests({ clientId, clientGeneration: String(gen), interests: trimmed });\n  } else throw error;\n}","preventionTips":["Deduplicate interests client-side before sending — duplicate keys waste budget.","Implement LRU eviction for client-side interest sets.","Only watch workspaces the user is actively viewing, not every workspace they can see.","Check the configured maxInterestsPerClient if you customized the coordinator."],"tags":["billing","capacity","validation"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}