{"record":{"id":"cfacb5cfe02a2bb6","repo":"musistudio/claude-code-router","slug":"new-api-subscription-response-did-not-include-quot","errorCode":null,"errorMessage":"New API subscription response did not include quota fields.","messagePattern":"New API subscription response did not include quota fields\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/electron/bundled-plugins/new-api-account/index.cjs","lineNumber":113,"sourceCode":"    readPath(data, [\"available_quota\"]),\n    readPath(data, [\"total_available\"]),\n    readPath(data, [\"balance\"])\n  );\n  const used = firstNumber(\n    readPath(data, [\"used_quota\"]),\n    readPath(data, [\"total_used\"]),\n    readPath(data, [\"used\"])\n  );\n  const configuredLimit = firstNumber(\n    readPath(data, [\"total_quota\"]),\n    readPath(data, [\"quota_total\"]),\n    readPath(data, [\"quota_limit\"]),\n    readPath(data, [\"total_granted\"]),\n    readPath(data, [\"limit\"])\n  );\n  const limit = configuredLimit ?? (remaining !== undefined && used !== undefined ? remaining + used : undefined);\n  if (remaining === undefined && used === undefined && limit === undefined) {\n    throw new Error(\"New API subscription response did not include quota fields.\");\n  }\n\n  return {\n    id: \"new_api_subscription_quota\",\n    kind: \"quota\",\n    label: readString(options.label) || \"Subscription quota\",\n    limit,\n    remaining,\n    resetAt: subscriptionResetAt(data),\n    unit: readString(options.unit) || \"quota\",\n    used\n  };\n}\n\nfunction subscriptionMessage(payload) {\n  const data = payloadData(payload);\n  return firstString(\n    readPath(data, [\"plan_name\"]),","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/electron/bundled-plugins/new-api-account/index.cjs#L95-L131","documentation":"subscriptionQuotaMeter derives remaining/used/limit from the subscription response via several candidate JSON paths; if all three are undefined it cannot compute any quota and throws. This indicates the response shape does not match any known quota schema.","triggerScenarios":"Provider returns a subscription payload without quota fields (free tier, different New API fork, changed field names like quota_remaining vs remaining), or response wrapped in an unexpected envelope so readPath misses.","commonSituations":"Switching provider forks (one-api/new-api variants), provider version upgrade renaming quota fields, or a stubbed test payload with only unrelated fields.","solutions":["Log the subscription response and identify the actual quota field names","Extend the readPath candidates (e.g. add ['data','quota_remaining']) to match the provider","Configure options to point at the correct subscriptionPath returning quota data","If the provider genuinely has no quota, skip quota metering for this account instead of throwing"],"exampleFix":"// before\nconst remaining = firstDefined(readPath(data, ['remaining']), readPath(data, ['quota_remaining']));\n\n// after\nconst remaining = firstDefined(\n  readPath(data, ['remaining']),\n  readPath(data, ['quota_remaining']),\n  readPath(data, ['data', 'remaining'])\n);","handlingStrategy":"fallback","validationCode":"const hasQuota = remaining !== undefined || used !== undefined || limit !== undefined;\nconst meter = hasQuota ? subscriptionQuotaMeter(data, options) : null; // skip metering instead of throwing","typeGuard":"null","tryCatchPattern":"catch (e) { if (/did not include quota fields/.test(e.message)) return null; throw e; }","preventionTips":["Keep readPath candidate list in sync with provider API docs","Make quota metering optional per provider","Snapshot real subscription payloads as fixtures"],"tags":["quota","provider-response","schema-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}