Skip to content

Comments

feat(ai-proxy): add UnauthorizedError/TooManyRequestsError and typed AI provider errors#1474

Merged
Scra3 merged 17 commits intomainfrom
feat/add-unauthorized-toomany-errors
Feb 25, 2026
Merged

feat(ai-proxy): add UnauthorizedError/TooManyRequestsError and typed AI provider errors#1474
Scra3 merged 17 commits intomainfrom
feat/add-unauthorized-toomany-errors

Conversation

@Scra3
Copy link
Member

@Scra3 Scra3 commented Feb 17, 2026

Summary

  • Add UnauthorizedError (401) and TooManyRequestsError (429) to agent-toolkit, re-exported from datasource-toolkit
  • Add httpCode property to BusinessError and all subclasses
  • Add corresponding cases in the agent's error-handling middleware
  • Create typed AI provider errors in ai-proxy:
    • AIProviderError — generic provider error (builds message from cause)
    • AITooManyRequestsError — wraps 429 from providers
    • AIUnauthorizedError — wraps 401 from providers
  • Refactor wrapProviderError to use typed errors instead of generic AIUnprocessableError
  • Add hierarchy tests for new error classes

@qltysh
Copy link

qltysh bot commented Feb 17, 2026

3 new issues

Tool Category Rule Count
qlty Structure Function with many returns (count = 9): getErrorStatus 2
qlty Structure Function with many parameters (count = 4): constructor 1

@qltysh
Copy link

qltysh bot commented Feb 17, 2026

Qlty

Coverage Impact

⬆️ Merging this pull request will increase total coverage on main by 0.01%.

Modified Files with Diff Coverage (6)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/provider-dispatcher.ts93.3%159
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/errors.ts100.0%
Coverage rating: A Coverage rating: A
packages/datasource-toolkit/src/errors.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent/src/routes/system/error-handling.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent-toolkit/src/errors.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent/src/types.ts100.0%
Total98.5%
🤖 Increase coverage with AI coding...

In the `feat/add-unauthorized-toomany-errors` branch, add test coverage for this new code:

- `packages/ai-proxy/src/provider-dispatcher.ts` -- Line 159

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

Base automatically changed from refactor/move-http-errors-to-agent-toolkit to main February 18, 2026 10:26
alban bertolini and others added 6 commits February 18, 2026 11:37
…AI provider errors

- Add UnauthorizedError (401) and TooManyRequestsError (429) to agent-toolkit
- Re-export from datasource-toolkit for backward compatibility
- Add HTTP codes and cases in agent error-handling middleware
- Create AIProviderError, AIRateLimitError, AIAuthenticationError in ai-proxy
  with baseBusinessErrorName overrides for correct HTTP status mapping
- Update wrapProviderError to use typed errors instead of generic AIUnprocessableError

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `status` property to BusinessError and all subclasses, simplifying
the error-handling middleware from a 30-line switch/case to a direct
status lookup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r class

The provider name and cause detail are now assembled by the constructor,
simplifying wrapProviderError callers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tibility

The simplified error.status lookup would break if the user upgrades
agent without upgrading datasource-toolkit (old errors lack the status
property).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rror, AIAuthenticationError

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Scra3 Scra3 force-pushed the feat/add-unauthorized-toomany-errors branch 2 times, most recently from d397d17 to 7057a1c Compare February 18, 2026 10:46
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Scra3 Scra3 force-pushed the feat/add-unauthorized-toomany-errors branch from 7057a1c to d189c01 Compare February 18, 2026 10:56
alban bertolini and others added 7 commits February 18, 2026 12:18
…r classes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ify constructor

- AIRateLimitError -> AITooManyRequestsError (matches TooManyRequestsError)
- AIAuthenticationError -> AIUnauthorizedError (matches UnauthorizedError)
- Remove message from AIProviderError options, build it from cause
- Type cause as Error instead of unknown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ts matching base HTTP error

Remove intermediate classes AIError and AIUnprocessableError. Each AI error now
directly extends its matching base class (e.g. AITooManyRequestsError extends
TooManyRequestsError, AIUnauthorizedError extends UnauthorizedError). This removes
manual baseBusinessErrorName/httpCode overrides and simplifies wrapProviderError
to a single instanceof BusinessError check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fields readonly

- Mark httpCode and baseBusinessErrorName as readonly on BusinessError
- Add AIForbiddenError (403) for provider permission-denied responses
- Include cause.message in AITooManyRequestsError and AIUnauthorizedError
  instead of hardcoded messages, with fallbacks
- Use JSON.stringify for non-Error throws in wrapProviderError

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eadonly compatibility

IntrospectionFormatError was assigning baseBusinessErrorName after super(),
which fails now that the field is readonly on BusinessError.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uctor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Scra3
Copy link
Member Author

Scra3 commented Feb 19, 2026

Impact on forestadmin-server — AI error translator binding

The file packages/private-api/src/domain/ai/error-translator-http.ts needs to be updated to use the new AI error types.

Current (broken after this PR)

import {
  AIError,               // ❌ removed
  AIToolNotFoundError,
  AIUnprocessableError,  // ❌ removed
  McpConfigError,
  McpConflictError,
  McpConnectionError,
} from '@forestadmin/ai-proxy';

After

import {
  AIForbiddenError,         // new (403)
  AIProviderError,          // replaces AIUnprocessableError (422)
  AITooManyRequestsError,   // new (429)
  AIToolNotFoundError,      // unchanged (404)
  AIUnauthorizedError,      // new (401)
  McpConfigError,           // unchanged (422)
  McpConflictError,         // unchanged (409)
  McpConnectionError,       // unchanged (424)
} from '@forestadmin/ai-proxy';

import {
  ConflictError,
  FailedDependencyError,
  ForbiddenError,
  NotFoundError,
  UnauthorizedError,
  UnprocessableEntityError,
} from '../../utils/errors/common-errors';

Updated switch

export default function aiErrorTranslator(error: unknown) {
  if (!error || !(error instanceof Error)) return error;

  switch (true) {
    case error instanceof AITooManyRequestsError:
      return new UnprocessableEntityError('AI rate limit', `AI: ${error.message}`);
      // Note: server TooManyRequestError requires retryAfter which we don't have
    case error instanceof AIUnauthorizedError:
      return new UnauthorizedError(`AI: ${error.message}`);
    case error instanceof AIForbiddenError:
      return new ForbiddenError(`AI: ${error.message}`);
    case error instanceof AIProviderError:
      return new UnprocessableEntityError('AI command', `AI: ${error.message}`);
    case error instanceof AIToolNotFoundError:
      return new NotFoundError(`AI: ${error.message}`);
    case error instanceof McpConnectionError:
      return new FailedDependencyError('MCP Connection Error', null, error);
    case error instanceof McpConfigError:
      return new UnprocessableEntityError('MCP config', `MCP Config Error: ${error.message}`);
    case error instanceof McpConflictError:
      return new ConflictError('MCP config', `MCP Config Error: ${error.message}`);
    default:
      return error;
  }
}

Key changes

  • AIError and AIUnprocessableError no longer exist → replaced by AIProviderError
  • 3 new error types: AITooManyRequestsError (429), AIUnauthorizedError (401), AIForbiddenError (403)
  • The AIError catch-all (→ InternalServerError) is removed — each AI error now maps to its dedicated HTTP type
  • AITooManyRequestsError mapped to UnprocessableEntityError for now because server's TooManyRequestError requires a retryAfter value we don't have from the provider

httpCode was never consumed — the error middleware uses instanceof/isOfType()
to map errors to HTTP status codes. Removing it simplifies the constructor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
alban bertolini and others added 2 commits February 24, 2026 16:08
… HTTP status

- Add InternalServerError to BusinessError hierarchy (agent-toolkit)
- Add AIProviderUnavailableError extending InternalServerError for 5xx provider errors
- Map provider 400 to AIBadRequestError instead of generic 422
- Safe JSON.stringify with try/catch fallback for non-Error throws
- Add baseBusinessErrorName assertions for all new AI error classes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Scra3 Scra3 merged commit 4800784 into main Feb 25, 2026
52 of 54 checks passed
@Scra3 Scra3 deleted the feat/add-unauthorized-toomany-errors branch February 25, 2026 08:57
forest-bot added a commit that referenced this pull request Feb 25, 2026
# @forestadmin/agent-toolkit [1.1.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/agent-toolkit@1.0.1...@forestadmin/agent-toolkit@1.1.0) (2026-02-25)

### Features

* **ai-proxy:** add UnauthorizedError/TooManyRequestsError and typed AI provider errors ([#1474](#1474)) ([4800784](4800784))
forest-bot added a commit that referenced this pull request Feb 25, 2026
# @forestadmin/datasource-toolkit [1.52.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/datasource-toolkit@1.51.1...@forestadmin/datasource-toolkit@1.52.0) (2026-02-25)

### Features

* **ai-proxy:** add UnauthorizedError/TooManyRequestsError and typed AI provider errors ([#1474](#1474)) ([4800784](4800784))

### Dependencies

* **@forestadmin/agent-toolkit:** upgraded to 1.1.0
forest-bot added a commit that referenced this pull request Feb 25, 2026
# @forestadmin/ai-proxy [1.6.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/ai-proxy@1.5.2...@forestadmin/ai-proxy@1.6.0) (2026-02-25)

### Features

* **ai-proxy:** add UnauthorizedError/TooManyRequestsError and typed AI provider errors ([#1474](#1474)) ([4800784](4800784))

### Dependencies

* **@forestadmin/agent-toolkit:** upgraded to 1.1.0
* **@forestadmin/datasource-toolkit:** upgraded to 1.52.0
forest-bot added a commit that referenced this pull request Feb 25, 2026
# @forestadmin/agent [1.74.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/agent@1.73.2...@forestadmin/agent@1.74.0) (2026-02-25)

### Features

* **ai-proxy:** add UnauthorizedError/TooManyRequestsError and typed AI provider errors ([#1474](#1474)) ([4800784](4800784))

### Dependencies

* **@forestadmin/agent-toolkit:** upgraded to 1.1.0
* **@forestadmin/datasource-customizer:** upgraded to 1.68.2
* **@forestadmin/datasource-toolkit:** upgraded to 1.52.0
* **@forestadmin/forestadmin-client:** upgraded to 1.37.16
* **@forestadmin/mcp-server:** upgraded to 1.8.7
* **@forestadmin/datasource-sql:** upgraded to 1.17.7
Sign up for free to 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.

2 participants