1+ /**
2+ * Shared provider HTTP error normalization helpers.
3+ *
4+ * Transport adapters use this module to turn provider-specific response bodies,
5+ * request ids, and binary payload guardrails into stable OpenClaw error shapes.
6+ */
17export { asFiniteNumber } from "../../packages/normalization-core/src/number-coercion.js" ;
28import { readResponseWithLimit } from "@openclaw/media-core/read-response-with-limit" ;
39import { normalizeOptionalString as trimToUndefined } from "../../packages/normalization-core/src/string-coerce.js" ;
@@ -8,20 +14,24 @@ export { normalizeOptionalString as trimToUndefined } from "../../packages/norma
814const ERROR_BODY_METADATA_LIMIT = 500 ;
915const PROVIDER_BINARY_RESPONSE_MAX_BYTES = 16 * 1024 * 1024 ;
1016
17+ /** Returns a plain object view for provider JSON payloads when one exists. */
1118export function asObject ( value : unknown ) : Record < string , unknown > | undefined {
1219 return typeof value === "object" && value !== null && ! Array . isArray ( value )
1320 ? ( value as Record < string , unknown > )
1421 : undefined ;
1522}
1623
24+ /** Trims provider error details to a log- and prompt-safe preview length. */
1725export function truncateErrorDetail ( detail : string , limit = 220 ) : string {
1826 return detail . length <= limit ? detail : `${ detail . slice ( 0 , limit - 1 ) } …` ;
1927}
2028
29+ /** Redacts secrets before preserving a bounded provider error body preview. */
2130export function redactProviderErrorBody ( body : string ) : string {
2231 return truncateErrorDetail ( redactSensitiveText ( body ) , ERROR_BODY_METADATA_LIMIT ) ;
2332}
2433
34+ /** Reads at most `limitBytes` from a response body without buffering provider-sized failures. */
2535export async function readResponseTextLimited (
2636 response : Response ,
2737 limitBytes = 16 * 1024 ,
@@ -64,13 +74,15 @@ export async function readResponseTextLimited(
6474 text += decoder . decode ( ) ;
6575 } finally {
6676 if ( reachedLimit ) {
77+ // Stop the upstream body once the diagnostic budget is full.
6778 await reader . cancel ( ) . catch ( ( ) => { } ) ;
6879 }
6980 }
7081
7182 return text ;
7283}
7384
85+ /** Formats common provider JSON error payload shapes into one readable detail string. */
7486export function formatProviderErrorPayload ( payload : unknown ) : string | undefined {
7587 const root = asObject ( payload ) ;
7688 const detailObject = asObject ( root ?. detail ) ;
@@ -132,6 +144,7 @@ function extractProviderErrorPayloadMetadata(payload: unknown): ProviderErrorPay
132144 } ;
133145}
134146
147+ /** Metadata extracted from a non-2xx provider response body and headers. */
135148export type ProviderHttpErrorInfo = {
136149 detail ?: string ;
137150 code ?: string ;
@@ -140,6 +153,7 @@ export type ProviderHttpErrorInfo = {
140153 requestId ?: string ;
141154} ;
142155
156+ /** Extracts normalized provider error metadata while keeping the raw body bounded and redacted. */
143157export async function extractProviderErrorInfo ( response : Response ) : Promise < ProviderHttpErrorInfo > {
144158 const rawBody = trimToUndefined ( await readResponseTextLimited ( response ) . catch ( ( ) => "" ) ) ;
145159 const requestId = extractProviderRequestId ( response ) ;
@@ -165,17 +179,20 @@ export async function extractProviderErrorInfo(response: Response): Promise<Prov
165179 }
166180}
167181
182+ /** Returns only the normalized provider detail string for callers that do not need metadata. */
168183export async function extractProviderErrorDetail ( response : Response ) : Promise < string | undefined > {
169184 return ( await extractProviderErrorInfo ( response ) ) . detail ;
170185}
171186
187+ /** Reads the provider request id header variants used across model and media APIs. */
172188export function extractProviderRequestId ( response : Response ) : string | undefined {
173189 return (
174190 trimToUndefined ( response . headers . get ( "x-request-id" ) ) ??
175191 trimToUndefined ( response . headers . get ( "request-id" ) )
176192 ) ;
177193}
178194
195+ /** Error type carrying normalized provider status, request id, code, type, and body metadata. */
179196export class ProviderHttpError extends Error {
180197 readonly status : number ;
181198 readonly statusCode : number ;
@@ -207,6 +224,7 @@ export class ProviderHttpError extends Error {
207224 }
208225}
209226
227+ /** Builds the human-facing provider HTTP error message from normalized metadata. */
210228export function formatProviderHttpErrorMessage ( params : {
211229 label : string ;
212230 status : number ;
@@ -222,6 +240,7 @@ export function formatProviderHttpErrorMessage(params: {
222240 ) ;
223241}
224242
243+ /** Creates a normalized provider HTTP error from a failed response. */
225244export async function createProviderHttpError (
226245 response : Response ,
227246 label : string ,
@@ -246,6 +265,7 @@ export async function createProviderHttpError(
246265 ) ;
247266}
248267
268+ /** Throws a normalized provider error when a fetch response is not OK. */
249269export async function assertOkOrThrowProviderError (
250270 response : Response ,
251271 label : string ,
@@ -256,13 +276,15 @@ export async function assertOkOrThrowProviderError(
256276 throw await createProviderHttpError ( response , label ) ;
257277}
258278
279+ /** Throws a normalized generic HTTP error when a fetch response is not OK. */
259280export async function assertOkOrThrowHttpError ( response : Response , label : string ) : Promise < void > {
260281 if ( response . ok ) {
261282 return ;
262283 }
263284 throw await createProviderHttpError ( response , label , { statusPrefix : "HTTP " } ) ;
264285}
265286
287+ /** Parses a provider JSON response and wraps malformed JSON with the caller's label. */
266288export async function readProviderJsonResponse < T > ( response : Response , label : string ) : Promise < T > {
267289 try {
268290 return ( await response . json ( ) ) as T ;
@@ -271,6 +293,7 @@ export async function readProviderJsonResponse<T>(response: Response, label: str
271293 }
272294}
273295
296+ /** Parses a provider JSON response that must be a top-level object. */
274297export async function readProviderJsonObjectResponse (
275298 response : Response ,
276299 label : string ,
@@ -283,6 +306,7 @@ export async function readProviderJsonObjectResponse(
283306 return object ;
284307}
285308
309+ /** Parses a provider JSON object response and returns an array field. */
286310export async function readProviderJsonArrayFieldResponse (
287311 response : Response ,
288312 label : string ,
@@ -301,6 +325,7 @@ function normalizeContentType(response: Response): string | undefined {
301325 return contentType || undefined ;
302326}
303327
328+ /** Rejects text or JSON responses on provider endpoints that should return binary bytes. */
304329export function assertProviderBinaryResponseContent (
305330 response : Response ,
306331 label : string ,
@@ -319,6 +344,7 @@ export function assertProviderBinaryResponseContent(
319344 }
320345}
321346
347+ /** Reads a bounded non-empty binary provider response after content-type validation. */
322348export async function readProviderBinaryResponse (
323349 response : Response ,
324350 label : string ,
0 commit comments