Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(runtime): classify usage-limit failures behind auth statuses as billing by yunaremaia · Pull Request #3660 · apache/maka · GitHub
Skip to content

fix(runtime): classify usage-limit failures behind auth statuses as billing - #3660

Merged
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth
Aug 24, 2026
Merged

fix(runtime): classify usage-limit failures behind auth statuses as billing#3660
Astro-Han merged 1 commit into
apache:mainfrom
yunaremaia:fix/2516-usage-limit-vs-auth

Conversation

@yunaremaia

Copy link
Copy Markdown
Contributor

Summary

Fixes#2516.

Providers report exhausted plan windows, credits, and subscriptions through different evidence channels, and the status-first fallback mislabeled all of them:

  • Some use explicit structured codes — OpenAI's insufficient_quota even arrives as 429, DeepSeek sends insufficient_balance.
  • Some gate the window behind credential-shaped 401/403 statuses for validly signed-in users (the Kimi Code plan case from the issue), which projected to Auth"Authentication failed" — pointing the user at re-authenticating when the useful action is waiting for the window to reset or checking the subscription.

Changes in packages/runtime/src/provider-error-classification.ts, at the shared classifier boundary (no provider-specific branches, no user-facing strings in adapters):

  • New PROVIDER_BILLING_PROVIDER_CODES set, checked with the other structured-code sets before every numeric HTTP fallback — explicit provider evidence outranks the bare status, the same precedence the capacity and overflow sets already follow. This also stops an exhausted quota arriving as 429 from being classified as a transient throttle.
  • The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the composite text: quota / usage-limit / plan / credit-exhaustion wording projects to ProviderBilling instead of Auth. Plain invalid-key and permission messages carry none of that vocabulary and stay Auth.
  • No retry-policy change needed: ProviderBilling already maps to a non-retryable policy in providerRetryMetadata, so closed plan windows stop being retried blindly while transient throttles keep their RateLimit path.
  • Redaction boundary unchanged — only the classification layer is touched; no raw bodies or tokens are surfaced anywhere new.

Verification

Local run this time (npm ci unblocked by pointing the six Azure DevOps mirror URLs in package-lock.json at their identical public npm packages; lockfile restored before commit):

  • npm run build -w @maka/core && npm run build -w @maka/storage && npm run build -w @maka/mcp && npm run build -w @maka/runtime — all pass
  • node --test dist/__tests__/provider-error-classification.test.js15 tests, 15 pass, 0 fail
  • Bite check: reverting only the implementation commit hunk makes the two new billing-projection tests fail (AuthProviderBilling) while the non-regression pins keep passing, then green again with the fix restored

New tests cover: structured usage-limit codes across 401/403/429 (+ non-retryable metadata), plan-window wording through both SDK carriers (error message and raw response body after a schema-parse failure), and genuine invalid-key / permission failures staying Auth.

One scope note: 429 responses whose text suggests a long cap but that carry no structured code still classify as RateLimit — distinguishing those would need per-provider cap wording I can't verify offline, and guessing risked false billing positives on real throttles.

…illing
Fixesapache#2516.
Providers report exhausted plan windows, credits, and subscriptions
through different evidence channels: some use explicit structured codes
(OpenAI insufficient_quota arrives even as 429; DeepSeek sends
insufficient_balance), and some gate the window behind credential-shaped
401/403 statuses for validly signed-in users, which the status-first
fallback projected to 'Authentication failed' — pointing the user at
re-authenticating when the useful action is waiting for the window to
reset or checking the subscription.
- New PROVIDER_BILLING_PROVIDER_CODES set checked with the other
structured-code sets, before every numeric HTTP fallback: explicit
provider evidence outranks the bare status (the same precedence the
capacity and overflow sets already follow).
- The 401/403 fallback now consults USAGE_LIMIT_TEXT_PATTERNS over the
composite text: quota/usage-limit/plan/credit-exhaustion wording
projects to ProviderBilling instead of Auth. Plain invalid-key and
permission messages carry none of that vocabulary and stay Auth.
- ProviderBilling already maps to a non-retryable policy in
providerRetryMetadata, so closed plan windows stop being retried
blindly while transient throttles keep their RateLimit path.
Tests: classifier matrix for structured codes across 401/403/429,
plan-window wording via both SDK carriers (error message and raw
response body after a schema-parse failure), and non-regression pins
for genuine invalid-key and permission failures.
Signed-off-by: Yunare Maia <yunare@gmail.com>

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3660f9b0dd8 — review (bind exact head)

Gate: CI test success on this head (run 32682534889, check_runs 1/1 success after approval). Fork PR previously blocked on action_required.

Verdict: GO (no P0-P2)

Classification narrowly fixes usage-limit behind 401/403: structured billing codes outrank status, and 401/403 fallback consults usage-limit text patterns over composite text, preserving genuine Auth. Non-retryable billing via providerRetryMetadata correct. Tests cover matrix.

Coexistence note: #2521 overlaps same defect file; #3660 is minimal fix shape. Epoch handling for any follow-up: rebase to current main and take strictly greater than base (do not hardcode number).

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at f9b0dd835713.

Gate at this exact head: hosted test is terminal green — note that this PR's checks had never run at all until the fork workflow was released (32682534889), so the earlier empty check-runs was "not run", not "passed". Zero review threads, no APPROVED review bound to any older commit, and the branch is mergeable.

The change stays inside the file that owns the defect: packages/runtime/src/provider-error-classification.ts and its test, +109/-1, classifying usage-limit failures behind the auth status rather than collapsing them into a generic auth error.

Worth recording for whoever handles this next: #2521 fixes the same defect (both branches are named for issue 2516, and both edit this same file and test), but does it across 46 files and +4428/-173. That one is separately marked NO-GO. If this lands first, #2521 will need to be rebased and reduced to whatever remains genuinely unaddressed.

Approval only; merging is a human's call.

@Astro-Han
Astro-Han merged commit 79fa4ac into apache:mainAug 24, 2026
1 check passed
@Astro-Han

Copy link
Copy Markdown
Contributor

LGTM — merged. Thanks for the contribution!

中文

已合并,感谢贡献。

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(runtime): distinguish usage limits from authentication failures

2 participants

@yunaremaia@Astro-Han