refactor(server): replace try/catch with neverthrow Result types#749
Open
Gengyscan wants to merge 1 commit intoMerit-Systems:masterfrom
Open
refactor(server): replace try/catch with neverthrow Result types#749Gengyscan wants to merge 1 commit intoMerit-Systems:masterfrom
Gengyscan wants to merge 1 commit intoMerit-Systems:masterfrom
Conversation
Systematic bottom-up migration of error handling from try/catch to neverthrow Result types across the server package. ## What changed - **New error types** (errors/results.ts): Discriminated union types (DbError, AuthError, SettleError, RefundError, ResourceError) for typed error handling throughout the codebase. - **Data layer** (DbService): All methods return ResultAsync<T, DbError> via ResultAsync.fromPromise(), eliminating try/catch at the DB boundary. - **Services** (EchoControlService, FreeTierService, HandleStreamService, ModelRequestService): Business logic uses neverthrow chains with .andThen() for composable error propagation. - **Handlers** (handlers.ts, settle.ts, refund.ts, resources/handler.ts): HTTP boundary uses .match() to map typed errors to appropriate status codes (401, 402, 429, 500). - **All providers** (AnthropicGPT, AnthropicNative, GPT, GeminiGPT, Gemini, Groq, OpenAIImage, OpenAIResponses, OpenRouter, XAI): handleBody() methods use ResultAsync.fromPromise() for consistent error handling across all LLM provider integrations. - **Resources** (e2b): Execution wrapped in ResultAsync. ## Migration strategy Bottom-up: DB -> Services -> Handlers -> Providers. Each layer returns typed Results that compose naturally via .andThen() chains. HTTP boundaries use .match() to convert errors to status codes. ## Remaining try/catch (29 of 62 original) Intentionally kept where neverthrow adds no value: - Top-level bootstrap (server.ts) - SSE parser defensive parsing in loops - Express route handlers (next(error) pattern) - Standalone client scripts (not runtime code) - Fetch timeout cleanup (facilitators) - Type definitions and test files ## Validation - sc --noEmit: 0 errors - No changes to public API contracts (401/402/429 responses preserved) - No changes to test files Closes Merit-Systems#581
Contributor
|
@Gengyscan is attempting to deploy a commit to the Merit Systems Team on Vercel. A member of the Team first needs to authorize it. |
Author
SummaryThis PR delivers a systematic, bottom-up migration from try/catch to neverthrow Result types across the entire server package. Results
Architecture
Files touched26 files, +1,238 / -1,084 lines — one clean squashed commit. Happy to discuss the approach, iterate on feedback, or walk through any specific conversion in detail. For faster communication, feel free to reach me on Discord: gmttgengyscang |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Systematic bottom-up migration of error handling from try/catch to neverthrow Result types across the server package. 62 try/catch blocks analyzed, 33 converted (53% reduction), 29 intentionally kept where try/catch is the correct pattern.
What changed
New error types (errors/results.ts)
Discriminated union types — DbError, AuthError, SettleError, RefundError, ResourceError — for typed error handling throughout the codebase. Each variant carries structured context for downstream handling.
Data layer (DbService)
All methods return ResultAsync<T, DbError> via ResultAsync.fromPromise(), replacing try/catch at the database boundary.
Services
Handlers
All HTTP boundaries preserve existing status code contracts (401, 402, 429, 500).
All providers (10 total)
handleBody() methods across all providers use ResultAsync.fromPromise():
AnthropicGPT, AnthropicNative, GPT, GeminiGPT, Gemini, Groq, OpenAIImage, OpenAIResponses, OpenRouter, XAI
Resources
Migration strategy
Bottom-up: DB → Services → Handlers → Providers
Each layer returns typed Results that compose naturally via .andThen() chains. HTTP handler boundaries use .match() to convert discriminated error types to appropriate HTTP status codes. Provider handleBody() methods use the transitional pattern (ResultAsync.fromPromise() + .match() throw) to maintain API compatibility while using neverthrow internally.
Remaining try/catch (29 of 62)
Intentionally preserved where neverthrow adds no value:
Validation
Closes #581