Skip to content

improvement(api): use HttpError base class for typed-error status mapping#4746

Merged
waleedlatif1 merged 2 commits into
stagingfrom
waleedlatif1/http-error-base-class
May 26, 2026
Merged

improvement(api): use HttpError base class for typed-error status mapping#4746
waleedlatif1 merged 2 commits into
stagingfrom
waleedlatif1/http-error-base-class

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • New HttpError abstract base class in lib/core/utils/http-error.ts
  • withRouteHandler now checks instanceof HttpError instead of duck-typing on statusCode — matches NestJS HttpException / Spring ResponseStatusException pattern
  • Eliminates the theoretical risk that a third-party error carrying a statusCode-shaped field could be promoted to a typed HTTP response and leak its raw message
  • WorkspaceAccessDeniedError and InvalidFieldError extend HttpError

Type of Change

  • Improvement

Testing

  • bun run check:api-validation passes
  • tsc --noEmit for apps/sim clean
  • Vitest: function/execute (35), mothership/chats (5), copilot/chats (8), copilot/chat/post (5), permissions utils (58), block-reference (23) — 134/134 pass

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 26, 2026 9:46pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 26, 2026

PR Summary

Low Risk
Small, targeted change to API error classification that reduces risk of leaking third-party error messages; behavior for existing HttpError subclasses should stay the same.

Overview
Introduces an abstract HttpError base class so domain errors that should map to HTTP responses are explicitly typed, following a NestJS/Spring-style pattern.

withRouteHandler now uses instanceof HttpError (instead of reading a statusCode property on any Error) when deciding whether to return the error’s message and status to the client. Untyped errors still get a generic 500.

WorkspaceAccessDeniedError (403) and InvalidFieldError (400) now extend HttpError so they continue to map correctly through the centralized wrapper without relying on duck-typing.

Reviewed by Cursor Bugbot for commit c77b8a3. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 26, 2026

Greptile Summary

This PR replaces duck-typing on a statusCode property with a proper instanceof HttpError check in withRouteHandler, closing the theoretical path where a third-party error carrying a statusCode-shaped field could leak its raw message to API clients.

  • Introduces HttpError as an abstract base class (abstract readonly statusCode: number) and migrates the two existing typed-error classes — WorkspaceAccessDeniedError (403) and InvalidFieldError (400) — to extend it.
  • readTypedErrorStatus now performs a type-safe instanceof HttpError guard and the previously redundant typeof status !== 'number' check is removed since TypeScript already narrows the type.
  • RateLimitError in lib/core/rate-limiter/types.ts still extends plain Error with a statusCode field; it is not currently thrown from any withRouteHandler-wrapped route, so there is no active regression, but it sits outside the new hierarchy.

Confidence Score: 5/5

Safe to merge; the change is a targeted, backward-compatible hardening of the route error handler with no altered behavior for existing callers.

Both migrated error classes retain the same statusCode values and message content, so HTTP responses are unchanged. PresignedUrlError is caught by a local try/catch and never reaches withRouteHandler. RateLimitError carries a statusCode field but is not thrown from any route handler in the current codebase, so no response code regresses to 500.

No files require special attention; the only note is lib/core/rate-limiter/types.ts, which defines RateLimitError outside the new HttpError hierarchy, but this class is not thrown from production route handlers today.

Important Files Changed

Filename Overview
apps/sim/lib/core/utils/http-error.ts New abstract HttpError base class with abstract readonly statusCode: number; clean, minimal, and well-documented.
apps/sim/lib/core/utils/with-route-handler.ts readTypedErrorStatus updated to use instanceof HttpError instead of duck-typing; typeof status check correctly removed; range guard retained.
apps/sim/executor/utils/block-reference.ts InvalidFieldError migrated to extend HttpError; import added; no functional change to error message or statusCode.
apps/sim/lib/workspaces/permissions/utils.ts WorkspaceAccessDeniedError migrated to extend HttpError; import added; no functional change to statusCode or message.

Sequence Diagram

sequenceDiagram
    participant Client
    participant withRouteHandler
    participant readTypedErrorStatus
    participant HttpError

    Client->>withRouteHandler: HTTP request
    withRouteHandler->>withRouteHandler: await handler(request, context)
    note over withRouteHandler: throws error

    withRouteHandler->>readTypedErrorStatus: readTypedErrorStatus(error)
    readTypedErrorStatus->>HttpError: error instanceof HttpError?
    alt is HttpError subclass (e.g. WorkspaceAccessDeniedError 403, InvalidFieldError 400)
        HttpError-->>readTypedErrorStatus: true
        readTypedErrorStatus->>readTypedErrorStatus: "check 400 <= statusCode < 600"
        readTypedErrorStatus-->>withRouteHandler: typed status (4xx/5xx)
        withRouteHandler-->>Client: "JSON { error: message } with typed status"
    else plain Error or third-party error
        HttpError-->>readTypedErrorStatus: false
        readTypedErrorStatus-->>withRouteHandler: undefined
        withRouteHandler-->>Client: "JSON { error: 'Internal server error' } 500"
    end
Loading

Reviews (2): Last reviewed commit: "chore(api): drop unreachable runtime che..." | Re-trigger Greptile

Comment thread apps/sim/lib/core/utils/with-route-handler.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c77b8a3. Configure here.

@waleedlatif1 waleedlatif1 merged commit 77c1d24 into staging May 26, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/http-error-base-class branch May 26, 2026 21:54
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.

1 participant