Skip to content

Commit 038c966

Browse files
committed
refactor: make error code field required and consolidate error handling
- Made PrintModeError.code field required (was optional) - Consolidated failure() and failureWithCode() into single failure() function - Updated ErrorObject to include required code field with optional status/originalError - Removed redundant failureWithCode() and ExtendedErrorObject type - Added standard ErrorCodes constants (AUTH_FAILED, NETWORK_ERROR, etc.) - Updated all error creation sites to include error codes - Eliminated all 'as any' type casting for error codes
1 parent 3d1771b commit 038c966

File tree

5 files changed

+43
-67
lines changed

5 files changed

+43
-67
lines changed

common/src/util/error.ts

Lines changed: 31 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export type ErrorObject = {
1616
name: string
1717
message: string
1818
stack?: string
19+
code: string
20+
status?: number
21+
originalError?: unknown
1922
}
2023

2124
export function success<T>(value: T): Success<T> {
@@ -26,85 +29,52 @@ export function success<T>(value: T): Success<T> {
2629
}
2730

2831
export function failure(error: any): Failure<ErrorObject> {
32+
if (error instanceof Error) {
33+
const base = getErrorObject(error)
34+
35+
// Safely extract code, status, and originalError if present
36+
const errorWithMetadata = error as Error & {
37+
code?: string
38+
status?: number
39+
originalError?: unknown
40+
}
41+
42+
return {
43+
success: false,
44+
error: {
45+
...base,
46+
code: typeof errorWithMetadata.code === 'string' ? errorWithMetadata.code : 'UNKNOWN_ERROR',
47+
...(typeof errorWithMetadata.status === 'number' && { status: errorWithMetadata.status }),
48+
...('originalError' in errorWithMetadata && { originalError: errorWithMetadata.originalError }),
49+
},
50+
}
51+
}
52+
2953
return {
3054
success: false,
31-
error: getErrorObject(error),
55+
error: {
56+
...getErrorObject(error),
57+
code: 'UNKNOWN_ERROR',
58+
},
3259
}
3360
}
3461

3562
export function getErrorObject(error: any): ErrorObject {
3663
if (error instanceof Error) {
64+
const errorWithCode = error as Error & { code?: string }
3765
return {
3866
name: error.name,
3967
message: error.message,
4068
stack: error.stack,
69+
code: typeof errorWithCode.code === 'string' ? errorWithCode.code : 'UNKNOWN_ERROR',
4170
}
4271
}
4372

4473
return {
4574
name: 'Error',
4675
message: `${error}`,
76+
code: 'UNKNOWN_ERROR',
4777
}
4878
}
4979

50-
/**
51-
* Extended error object that can preserve additional metadata like code, status, and originalError
52-
* from Error instances (e.g., NetworkError, AuthenticationError).
53-
*/
54-
export type ExtendedErrorObject = ErrorObject & {
55-
code?: string
56-
status?: number
57-
originalError?: unknown
58-
}
59-
60-
/**
61-
* Wrap an unknown error into a Failure<ExtendedErrorObject>, preserving `code`, `status`, and `originalError`
62-
* when present on Error instances.
63-
*
64-
* This is useful for converting thrown errors into ErrorOr results while maintaining error metadata.
65-
*
66-
* @example
67-
* ```typescript
68-
* try {
69-
* await somethingThatMightThrow()
70-
* return success(result)
71-
* } catch (error) {
72-
* return failureWithCode(error)
73-
* }
74-
* ```
75-
*/
76-
export function failureWithCode(error: unknown): Failure<ExtendedErrorObject> {
77-
if (error instanceof Error) {
78-
const base = getErrorObject(error)
79-
const enriched: ExtendedErrorObject = {
80-
...base,
81-
}
82-
83-
// Safely extract code, status, and originalError if present
84-
const errorWithMetadata = error as Error & {
85-
code?: string
86-
status?: number
87-
originalError?: unknown
88-
}
89-
90-
if (typeof errorWithMetadata.code === 'string') {
91-
enriched.code = errorWithMetadata.code
92-
}
9380

94-
if (typeof errorWithMetadata.status === 'number') {
95-
enriched.status = errorWithMetadata.status
96-
}
97-
98-
if ('originalError' in errorWithMetadata) {
99-
enriched.originalError = errorWithMetadata.originalError
100-
}
101-
102-
return {
103-
success: false,
104-
error: enriched,
105-
}
106-
}
107-
108-
// Fallback to base failure, which will still give us an ErrorObject
109-
return failure(error)
110-
}

npm-app/src/cli.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ export class CLI {
297297
printModeLog({
298298
type: 'error',
299299
message: errorMessage,
300+
code: 'UNHANDLED_REJECTION',
300301
})
301302
}
302303
console.error(`\n${errorMessage}`)
@@ -319,6 +320,7 @@ export class CLI {
319320
printModeLog({
320321
type: 'error',
321322
message: errorMessage,
323+
code: 'UNCAUGHT_EXCEPTION',
322324
})
323325
}
324326
console.error(`\n${errorMessage}`)
@@ -778,6 +780,7 @@ export class CLI {
778780
printModeLog({
779781
type: 'error',
780782
message: `Print mode requires authentication. Please run "codebuff login" or set the ${API_KEY_ENV_VAR} environment variable first.`,
783+
code: 'AUTH_FAILED',
781784
})
782785
process.exit(1)
783786
}
@@ -1330,6 +1333,7 @@ export class CLI {
13301333
printModeLog({
13311334
type: 'error',
13321335
message: 'Could not connect to server.',
1336+
code: 'NETWORK_ERROR',
13331337
})
13341338
process.exit(1)
13351339
}

npm-app/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ For all commands and options, run 'codebuff' and then type 'help'.
279279
printModeLog({
280280
type: 'error',
281281
message: 'Error: Print mode requires a prompt to be set',
282+
code: 'VALIDATION_ERROR',
282283
})
283284
process.exit(1)
284285
}

sdk/src/impl/database-safe.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { getUserInfoFromApiKey } from './database'
22
import {
3-
failureWithCode,
4-
type ExtendedErrorObject,
3+
failure,
4+
type ErrorObject,
55
type ErrorOr,
66
} from '@codebuff/common/util/error'
77
import type {
@@ -15,7 +15,7 @@ type User = {
1515
discord_id: string | null
1616
}
1717

18-
export type GetUserInfoFromApiKeySafeError = ExtendedErrorObject
18+
export type GetUserInfoFromApiKeySafeError = ErrorObject
1919

2020
export async function getUserInfoFromApiKeySafe<T extends UserColumn>(
2121
params: GetUserInfoFromApiKeyInput<T>,
@@ -27,6 +27,6 @@ export async function getUserInfoFromApiKeySafe<T extends UserColumn>(
2727
value: result,
2828
}
2929
} catch (error) {
30-
return failureWithCode(error)
30+
return failure(error)
3131
}
3232
}

sdk/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ export type { ValidationResult, ValidateAgentsOptions } from './validate-agents'
4545

4646
// ErrorOr utilities
4747
export {
48-
failureWithCode,
49-
type ExtendedErrorObject,
48+
failure,
49+
success,
50+
type ErrorObject,
5051
type ErrorOr,
5152
type Success,
5253
type Failure,

0 commit comments

Comments
 (0)