Skip to content

Commit 285dece

Browse files
committed
Include more comment error object fields in logs
1 parent c10508f commit 285dece

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

common/src/util/error.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ export type ErrorObject = {
2424
code?: string
2525
/** Optional raw error object */
2626
rawError?: string
27+
/** Response body from API errors (AI SDK APICallError) */
28+
responseBody?: string
29+
/** URL that was called (API errors) */
30+
url?: string
31+
/** Whether the error is retryable (API errors) */
32+
isRetryable?: boolean
33+
/** Request body values that were sent (API errors) - stringified for safety */
34+
requestBodyValues?: string
35+
/** Cause of the error, if nested */
36+
cause?: ErrorObject
2737
}
2838

2939
export function success<T>(value: T): Success<T> {
@@ -45,6 +55,37 @@ interface ExtendedErrorProperties {
4555
status?: number
4656
statusCode?: number
4757
code?: string
58+
// API error properties (AI SDK APICallError, etc.)
59+
responseBody?: string
60+
url?: string
61+
isRetryable?: boolean
62+
requestBodyValues?: Record<string, unknown>
63+
cause?: unknown
64+
}
65+
66+
/**
67+
* Safely stringify an object, handling circular references and large objects.
68+
*/
69+
function safeStringify(value: unknown, maxLength = 10000): string | undefined {
70+
if (value === undefined || value === null) return undefined
71+
if (typeof value === 'string') return value.slice(0, maxLength)
72+
try {
73+
const seen = new WeakSet()
74+
const str = JSON.stringify(
75+
value,
76+
(_, val) => {
77+
if (typeof val === 'object' && val !== null) {
78+
if (seen.has(val)) return '[Circular]'
79+
seen.add(val)
80+
}
81+
return val
82+
},
83+
2,
84+
)
85+
return str?.slice(0, maxLength)
86+
} catch {
87+
return '[Unable to stringify]'
88+
}
4889
}
4990

5091
export function getErrorObject(
@@ -53,6 +94,28 @@ export function getErrorObject(
5394
): ErrorObject {
5495
if (error instanceof Error) {
5596
const extError = error as Error & Partial<ExtendedErrorProperties>
97+
98+
// Extract responseBody - could be string or object
99+
let responseBody: string | undefined
100+
if (extError.responseBody !== undefined) {
101+
responseBody = safeStringify(extError.responseBody)
102+
}
103+
104+
// Extract requestBodyValues - typically an object, stringify for logging
105+
let requestBodyValues: string | undefined
106+
if (
107+
extError.requestBodyValues !== undefined &&
108+
typeof extError.requestBodyValues === 'object'
109+
) {
110+
requestBodyValues = safeStringify(extError.requestBodyValues)
111+
}
112+
113+
// Extract cause - recursively convert to ErrorObject if present
114+
let cause: ErrorObject | undefined
115+
if (extError.cause !== undefined) {
116+
cause = getErrorObject(extError.cause, options)
117+
}
118+
56119
return {
57120
name: error.name,
58121
message: error.message,
@@ -64,8 +127,17 @@ export function getErrorObject(
64127
: undefined,
65128
code: typeof extError.code === 'string' ? extError.code : undefined,
66129
rawError: options.includeRawError
67-
? JSON.stringify(error, null, 2)
130+
? safeStringify(error)
68131
: undefined,
132+
// API error fields
133+
responseBody,
134+
url: typeof extError.url === 'string' ? extError.url : undefined,
135+
isRetryable:
136+
typeof extError.isRetryable === 'boolean'
137+
? extError.isRetryable
138+
: undefined,
139+
requestBodyValues,
140+
cause,
69141
}
70142
}
71143

0 commit comments

Comments
 (0)