@apifuse/provider-sdk 2.2.0-beta.40 → 2.2.0-beta.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/bin/apifuse-check.ts +61 -0
- package/bin/apifuse-migrate-shape.ts +84 -0
- package/bin/apifuse-submit-check.ts +1760 -222
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.js +6 -0
- package/dist/cli/migrate-provider-shape.d.ts +52 -0
- package/dist/cli/migrate-provider-shape.js +515 -0
- package/dist/cli/templates/provider/provider.json.tpl +6 -0
- package/dist/contract.js +1 -0
- package/dist/define.js +22 -1
- package/dist/error-observability.d.ts +7 -0
- package/dist/error-observability.js +61 -0
- package/dist/errors.d.ts +15 -0
- package/dist/fixture-sanitization.js +13 -3
- package/dist/index.d.ts +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/runtime/executor.js +11 -1
- package/dist/server/error-observability.d.ts +1 -0
- package/dist/server/error-observability.js +1 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/self-test.js +3 -0
- package/dist/server/serve-implementation.d.ts +12 -0
- package/dist/server/serve-implementation.js +135 -66
- package/dist/types.d.ts +18 -10
- package/package.json +3 -3
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +6 -0
- package/src/cli/migrate-provider-shape.ts +701 -0
- package/src/cli/templates/provider/provider.json.tpl +6 -0
- package/src/contract.ts +1 -0
- package/src/define.ts +33 -1
- package/src/error-observability.ts +64 -0
- package/src/errors.ts +16 -0
- package/src/fixture-sanitization.ts +19 -3
- package/src/index.ts +1 -0
- package/src/provider.ts +1 -0
- package/src/runtime/executor.ts +13 -1
- package/src/server/error-observability.ts +1 -0
- package/src/server/index.ts +2 -0
- package/src/server/self-test.ts +5 -0
- package/src/server/serve-implementation.ts +172 -84
- package/src/types.ts +38 -27
|
@@ -7,6 +7,7 @@ import { Hono } from "hono";
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
|
|
9
9
|
import { validateFailClosedDeclaration } from "../declaration-validation.js";
|
|
10
|
+
import { safeProviderErrorObservability } from "../error-observability.js";
|
|
10
11
|
import {
|
|
11
12
|
SDK_OWNED_PROVIDER_ERROR_CODES,
|
|
12
13
|
SDK_RUNTIME_OWNED_ERROR_CODES,
|
|
@@ -19,7 +20,10 @@ import {
|
|
|
19
20
|
isTransportError,
|
|
20
21
|
isValidationError,
|
|
21
22
|
ProviderError,
|
|
23
|
+
type ProviderErrorObservability,
|
|
24
|
+
type ProviderErrorOptions,
|
|
22
25
|
} from "../errors.js";
|
|
26
|
+
import { sanitizeDiagnosticText } from "../fixture-sanitization.js";
|
|
23
27
|
import {
|
|
24
28
|
loadProviderLocaleCatalogs,
|
|
25
29
|
localizeAuthTurn,
|
|
@@ -140,7 +144,36 @@ export type ErrorObservabilityDetails = {
|
|
|
140
144
|
taxonomyVersion: string;
|
|
141
145
|
retryable: boolean;
|
|
142
146
|
upstreamStatus?: number;
|
|
147
|
+
providerObservability?: ProviderErrorObservability;
|
|
143
148
|
};
|
|
149
|
+
|
|
150
|
+
// Provider errors normally expose `options` through an own data property, but
|
|
151
|
+
// providers can replace that property with a throwing accessor. Provider-controlled
|
|
152
|
+
// accessor failures are absorbed into canonical classification rather than
|
|
153
|
+
// propagated. By contract, read `options` directly instead of the public
|
|
154
|
+
// `fix`/`details` convenience getters; provider-controlled accessors are not trusted.
|
|
155
|
+
function providerErrorOption<K extends keyof ProviderErrorOptions>(
|
|
156
|
+
error: unknown,
|
|
157
|
+
key: K,
|
|
158
|
+
): ProviderErrorOptions[K] | undefined {
|
|
159
|
+
if (!isProviderError(error)) return undefined;
|
|
160
|
+
try {
|
|
161
|
+
return (error as ProviderError).options?.[key] as ProviderErrorOptions[K] | undefined;
|
|
162
|
+
} catch {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function providerErrorCode(error: unknown): string | undefined {
|
|
168
|
+
if (!isProviderError(error)) return undefined;
|
|
169
|
+
try {
|
|
170
|
+
const code: unknown = (error as ProviderError).code;
|
|
171
|
+
return typeof code === "string" ? code : undefined;
|
|
172
|
+
} catch {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
144
177
|
const AUTH_FLOW_LOCALES = ["en", "ko", "ja"] as const;
|
|
145
178
|
const retryResponseMeta = new WeakMap<ProviderContext, HttpRetrySummary>();
|
|
146
179
|
const STATEFUL_INTERNAL_OPERATIONS_ROUTE = "/__apifuse/stateful/operations";
|
|
@@ -989,6 +1022,8 @@ export type ProviderServerLogEvent =
|
|
|
989
1022
|
errorCategory?: ProviderErrorCategory;
|
|
990
1023
|
taxonomyVersion?: string;
|
|
991
1024
|
retryable?: boolean;
|
|
1025
|
+
providerObservability?: ProviderErrorObservability;
|
|
1026
|
+
causeChain?: ProviderErrorCauseFrame[];
|
|
992
1027
|
signal?: "unregistered_provider_error_code";
|
|
993
1028
|
signalFix?: string;
|
|
994
1029
|
issues?: Array<{ path: string; code: string; message: string }>;
|
|
@@ -1135,8 +1170,9 @@ function zodDetails(error: z.ZodError): Array<{
|
|
|
1135
1170
|
function publicErrorSource(error: unknown, category: ProviderErrorCategory): ProviderErrorSource {
|
|
1136
1171
|
if (error instanceof StatefulRoutingDeadlineError) return "apifuse";
|
|
1137
1172
|
if (isProviderError(error)) {
|
|
1138
|
-
|
|
1139
|
-
if (
|
|
1173
|
+
const code = providerErrorCode(error);
|
|
1174
|
+
if (code === MISSING_SECRET_CODE) return "apifuse";
|
|
1175
|
+
if (code === "UPSTREAM_ERROR" || code === "BLOCKED") {
|
|
1140
1176
|
return "upstream_failure";
|
|
1141
1177
|
}
|
|
1142
1178
|
}
|
|
@@ -1145,10 +1181,10 @@ function publicErrorSource(error: unknown, category: ProviderErrorCategory): Pro
|
|
|
1145
1181
|
|
|
1146
1182
|
function toErrorResponse(
|
|
1147
1183
|
error: unknown,
|
|
1148
|
-
requestId
|
|
1149
|
-
|
|
1184
|
+
requestId: string | undefined,
|
|
1185
|
+
observabilityDetails: ErrorObservabilityDetails,
|
|
1150
1186
|
): OperationErrorResponse {
|
|
1151
|
-
const observability =
|
|
1187
|
+
const observability = observabilityDetails;
|
|
1152
1188
|
const source = publicErrorSource(error, observability.category);
|
|
1153
1189
|
if (error instanceof StatefulRoutingDeadlineError) {
|
|
1154
1190
|
return {
|
|
@@ -1163,15 +1199,15 @@ function toErrorResponse(
|
|
|
1163
1199
|
}
|
|
1164
1200
|
|
|
1165
1201
|
if (isProviderError(error)) {
|
|
1166
|
-
const details = error
|
|
1202
|
+
const details = providerErrorOption(error, "details");
|
|
1167
1203
|
return {
|
|
1168
1204
|
error: {
|
|
1169
|
-
code: error
|
|
1205
|
+
code: providerErrorCode(error) ?? "provider_error",
|
|
1170
1206
|
message: publicProviderErrorMessage(error),
|
|
1171
1207
|
...(requestId ? { requestId } : {}),
|
|
1172
1208
|
retryable: observability.retryable,
|
|
1173
1209
|
source,
|
|
1174
|
-
...(error
|
|
1210
|
+
...(providerErrorOption(error, "fix") ? { fix: providerErrorOption(error, "fix") } : {}),
|
|
1175
1211
|
...(details !== undefined ? { details } : {}),
|
|
1176
1212
|
},
|
|
1177
1213
|
};
|
|
@@ -1231,9 +1267,9 @@ function providerObservabilityDetails(
|
|
|
1231
1267
|
// signal for exactly the retryOnAuthRefresh operations it is meant to enable.
|
|
1232
1268
|
if (isSessionExpiredError(error)) {
|
|
1233
1269
|
return {
|
|
1234
|
-
category: error
|
|
1270
|
+
category: providerErrorOption(error, "category") ?? "credential_expired",
|
|
1235
1271
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1236
|
-
retryable: error
|
|
1272
|
+
retryable: providerErrorOption(error, "retryable") ?? declaredRetryable ?? false,
|
|
1237
1273
|
};
|
|
1238
1274
|
}
|
|
1239
1275
|
// Missing-secret errors carry the canonical credential_unavailable category
|
|
@@ -1241,29 +1277,29 @@ function providerObservabilityDetails(
|
|
|
1241
1277
|
// the upstream. Matched by code (not constructor) so both the SDK-owned
|
|
1242
1278
|
// runtime gate and any not-yet-migrated provider-thrown MISSING_SECRET
|
|
1243
1279
|
// serialize identically, including across duplicate SDK module instances.
|
|
1244
|
-
if (isProviderError(error) && error
|
|
1280
|
+
if (isProviderError(error) && providerErrorCode(error) === MISSING_SECRET_CODE) {
|
|
1245
1281
|
return {
|
|
1246
|
-
category: error
|
|
1282
|
+
category: providerErrorOption(error, "category") ?? "credential_unavailable",
|
|
1247
1283
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1248
|
-
retryable: error
|
|
1284
|
+
retryable: providerErrorOption(error, "retryable") ?? declaredRetryable ?? false,
|
|
1249
1285
|
};
|
|
1250
1286
|
}
|
|
1251
1287
|
if (!isTransportError(error)) {
|
|
1252
1288
|
return undefined;
|
|
1253
1289
|
}
|
|
1254
1290
|
const isProxyPoolCode =
|
|
1255
|
-
error
|
|
1256
|
-
error
|
|
1257
|
-
error
|
|
1291
|
+
providerErrorCode(error) === PROXY_POOL_EXHAUSTED_CODE ||
|
|
1292
|
+
providerErrorCode(error) === PROXY_EDGE_AUTH_REJECTED_CODE ||
|
|
1293
|
+
providerErrorCode(error) === "PROXY_ALLOCATION_FAILED";
|
|
1258
1294
|
const category =
|
|
1259
|
-
error
|
|
1295
|
+
providerErrorOption(error, "category") ??
|
|
1260
1296
|
(isProxyPoolCode
|
|
1261
1297
|
? "proxy_pool"
|
|
1262
|
-
: error
|
|
1298
|
+
: providerErrorCode(error) === PROXY_AUTH_IP_DENIED_CODE
|
|
1263
1299
|
? "anti_bot_blocked"
|
|
1264
|
-
: error
|
|
1300
|
+
: providerErrorCode(error) === "transport_timeout"
|
|
1265
1301
|
? "timeout"
|
|
1266
|
-
: error
|
|
1302
|
+
: providerErrorCode(error) === "transport_network_error"
|
|
1267
1303
|
? "network"
|
|
1268
1304
|
: error.upstreamStatus
|
|
1269
1305
|
? categoryForStatus(error.upstreamStatus)
|
|
@@ -1272,7 +1308,7 @@ function providerObservabilityDetails(
|
|
|
1272
1308
|
category,
|
|
1273
1309
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1274
1310
|
retryable:
|
|
1275
|
-
error
|
|
1311
|
+
providerErrorOption(error, "retryable") ??
|
|
1276
1312
|
(category === "upstream_http" && error.upstreamStatus
|
|
1277
1313
|
? error.upstreamStatus >= 500
|
|
1278
1314
|
: isRetryableCategory(category)),
|
|
@@ -1280,7 +1316,7 @@ function providerObservabilityDetails(
|
|
|
1280
1316
|
};
|
|
1281
1317
|
}
|
|
1282
1318
|
|
|
1283
|
-
function
|
|
1319
|
+
function classifiedErrorObservabilityDetails(
|
|
1284
1320
|
error: unknown,
|
|
1285
1321
|
declaredErrorCode?: OperationErrorCode,
|
|
1286
1322
|
): ErrorObservabilityDetails {
|
|
@@ -1290,19 +1326,19 @@ function errorObservabilityDetails(
|
|
|
1290
1326
|
|
|
1291
1327
|
if (error instanceof z.ZodError || isValidationError(error)) {
|
|
1292
1328
|
const declaredStatus = effectiveDeclaration?.status;
|
|
1329
|
+
const providerCategory = providerErrorOption(error, "category");
|
|
1293
1330
|
return {
|
|
1294
|
-
category:
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
: "input_validation",
|
|
1331
|
+
category: providerCategory
|
|
1332
|
+
? providerCategory
|
|
1333
|
+
: isEmittableErrorStatus(declaredStatus) &&
|
|
1334
|
+
categoryForStatus(declaredStatus) === "upstream_rejected"
|
|
1335
|
+
? "upstream_rejected"
|
|
1336
|
+
: isEmittableErrorStatus(declaredStatus) && declaredStatus >= 500
|
|
1337
|
+
? "provider_error"
|
|
1338
|
+
: "input_validation",
|
|
1303
1339
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1304
1340
|
retryable: isProviderError(error)
|
|
1305
|
-
? (error
|
|
1341
|
+
? (providerErrorOption(error, "retryable") ?? effectiveDeclaration?.retryable ?? false)
|
|
1306
1342
|
: false,
|
|
1307
1343
|
};
|
|
1308
1344
|
}
|
|
@@ -1322,15 +1358,16 @@ function errorObservabilityDetails(
|
|
|
1322
1358
|
// author set an explicit category.
|
|
1323
1359
|
const declaredStatus = effectiveDeclaration?.status;
|
|
1324
1360
|
const rejectionDefault =
|
|
1325
|
-
error
|
|
1361
|
+
providerErrorCode(error) === "UPSTREAM_REJECTED" ||
|
|
1326
1362
|
(isEmittableErrorStatus(declaredStatus) &&
|
|
1327
1363
|
categoryForStatus(declaredStatus) === "upstream_rejected")
|
|
1328
1364
|
? ("upstream_rejected" as const)
|
|
1329
1365
|
: ("provider_error" as const);
|
|
1330
1366
|
return {
|
|
1331
|
-
category: error
|
|
1367
|
+
category: providerErrorOption(error, "category") ?? rejectionDefault,
|
|
1332
1368
|
taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
|
|
1333
|
-
retryable:
|
|
1369
|
+
retryable:
|
|
1370
|
+
providerErrorOption(error, "retryable") ?? effectiveDeclaration?.retryable ?? false,
|
|
1334
1371
|
};
|
|
1335
1372
|
}
|
|
1336
1373
|
|
|
@@ -1341,16 +1378,24 @@ function errorObservabilityDetails(
|
|
|
1341
1378
|
};
|
|
1342
1379
|
}
|
|
1343
1380
|
|
|
1344
|
-
function
|
|
1345
|
-
response: Response,
|
|
1381
|
+
function errorObservabilityDetails(
|
|
1346
1382
|
error: unknown,
|
|
1347
1383
|
declaredErrorCode?: OperationErrorCode,
|
|
1384
|
+
): ErrorObservabilityDetails {
|
|
1385
|
+
const details = classifiedErrorObservabilityDetails(error, declaredErrorCode);
|
|
1386
|
+
const providerObservability = safeProviderErrorObservability(error);
|
|
1387
|
+
return {
|
|
1388
|
+
...details,
|
|
1389
|
+
...(providerObservability ? { providerObservability } : {}),
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function responseWithErrorObservability(
|
|
1394
|
+
response: Response,
|
|
1395
|
+
observabilityDetails: ErrorObservabilityDetails,
|
|
1348
1396
|
): Response {
|
|
1349
1397
|
const headers = new Headers(response.headers);
|
|
1350
|
-
headers.set(
|
|
1351
|
-
ERROR_OBSERVABILITY_HEADER,
|
|
1352
|
-
JSON.stringify(errorObservabilityDetails(error, declaredErrorCode)),
|
|
1353
|
-
);
|
|
1398
|
+
headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(observabilityDetails));
|
|
1354
1399
|
return new Response(response.body, {
|
|
1355
1400
|
status: response.status,
|
|
1356
1401
|
statusText: response.statusText,
|
|
@@ -1360,18 +1405,18 @@ function responseWithErrorObservability(
|
|
|
1360
1405
|
|
|
1361
1406
|
function publicProviderErrorMessage(error: ProviderError): string {
|
|
1362
1407
|
if (isTransportError(error)) {
|
|
1363
|
-
if (error
|
|
1408
|
+
if (providerErrorCode(error) === PROXY_AUTH_IP_DENIED_CODE) {
|
|
1364
1409
|
return error.message;
|
|
1365
1410
|
}
|
|
1366
|
-
if (error
|
|
1411
|
+
if (providerErrorCode(error) === PROXY_EDGE_AUTH_REJECTED_CODE) {
|
|
1367
1412
|
return error.message;
|
|
1368
1413
|
}
|
|
1369
|
-
if (error
|
|
1414
|
+
if (providerErrorCode(error) === PROXY_POOL_EXHAUSTED_CODE) {
|
|
1370
1415
|
return error.message;
|
|
1371
1416
|
}
|
|
1372
|
-
if (error
|
|
1373
|
-
if (error
|
|
1374
|
-
if (error
|
|
1417
|
+
if (providerErrorCode(error) === "transport_timeout") return "Request timed out";
|
|
1418
|
+
if (providerErrorCode(error) === "transport_network_error") return "Network error";
|
|
1419
|
+
if (providerErrorCode(error) === "upstream_http_error" && error.status) {
|
|
1375
1420
|
return `Upstream request failed with status ${error.status}`;
|
|
1376
1421
|
}
|
|
1377
1422
|
if (error.status) {
|
|
@@ -1401,17 +1446,18 @@ function toStatusCode(error: unknown, declaredErrorCode?: OperationErrorCode): P
|
|
|
1401
1446
|
}
|
|
1402
1447
|
// Canonical SDK code → status mapping lives in error-resolution.ts so
|
|
1403
1448
|
// the authoring lint and this runtime path share one source of truth.
|
|
1404
|
-
|
|
1405
|
-
|
|
1449
|
+
const code = providerErrorCode(error);
|
|
1450
|
+
if (typeof code === "string") {
|
|
1451
|
+
const mappedStatus = SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES.get(code);
|
|
1406
1452
|
if (mappedStatus !== undefined) {
|
|
1407
1453
|
return mappedStatus;
|
|
1408
1454
|
}
|
|
1409
1455
|
}
|
|
1410
1456
|
if (isTransportError(error)) {
|
|
1411
|
-
return error
|
|
1457
|
+
return providerErrorCode(error) === "transport_timeout" ? 504 : 502;
|
|
1412
1458
|
}
|
|
1413
1459
|
if (isValidationError(error)) {
|
|
1414
|
-
return error
|
|
1460
|
+
return providerErrorOption(error, "category") === "output_validation" ? 500 : 400;
|
|
1415
1461
|
}
|
|
1416
1462
|
|
|
1417
1463
|
return 500;
|
|
@@ -1425,11 +1471,9 @@ function sdkOwnsErrorResolution(error: unknown): boolean {
|
|
|
1425
1471
|
if (isTransportError(error)) return true;
|
|
1426
1472
|
if (error instanceof z.ZodError) return true;
|
|
1427
1473
|
if (error instanceof StatefulRoutingDeadlineError) return true;
|
|
1428
|
-
return
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
SDK_RUNTIME_OWNED_ERROR_CODES.has(error.code)
|
|
1432
|
-
);
|
|
1474
|
+
if (!isProviderError(error)) return false;
|
|
1475
|
+
const code = providerErrorCode(error);
|
|
1476
|
+
return typeof code === "string" && SDK_RUNTIME_OWNED_ERROR_CODES.has(code);
|
|
1433
1477
|
}
|
|
1434
1478
|
|
|
1435
1479
|
type OperationErrorCodeLookup = ReadonlyMap<string, ReadonlyMap<string, OperationErrorCode>>;
|
|
@@ -1450,8 +1494,9 @@ function declaredErrorCodeFor(
|
|
|
1450
1494
|
operationId: string | undefined,
|
|
1451
1495
|
lookup: OperationErrorCodeLookup,
|
|
1452
1496
|
): OperationErrorCode | undefined {
|
|
1453
|
-
|
|
1454
|
-
|
|
1497
|
+
const code = providerErrorCode(error);
|
|
1498
|
+
if (!operationId || !code) return undefined;
|
|
1499
|
+
return lookup.get(operationId)?.get(code);
|
|
1455
1500
|
}
|
|
1456
1501
|
|
|
1457
1502
|
function extractRequestId(raw: unknown): string | undefined {
|
|
@@ -1463,14 +1508,24 @@ function extractRequestId(raw: unknown): string | undefined {
|
|
|
1463
1508
|
return typeof value === "string" ? value : undefined;
|
|
1464
1509
|
}
|
|
1465
1510
|
|
|
1466
|
-
type ProviderErrorCauseFrame = {
|
|
1511
|
+
export type ProviderErrorCauseFrame = {
|
|
1467
1512
|
errorClass: string;
|
|
1468
1513
|
code?: string;
|
|
1514
|
+
message: string;
|
|
1469
1515
|
messageLength: number;
|
|
1470
1516
|
messageFingerprint: string;
|
|
1517
|
+
providerObservability?: ProviderErrorObservability;
|
|
1471
1518
|
};
|
|
1472
1519
|
|
|
1473
1520
|
const MAX_PROVIDER_ERROR_CAUSE_FRAMES = 5;
|
|
1521
|
+
const MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH = 300;
|
|
1522
|
+
|
|
1523
|
+
function providerErrorCauseMessage(message: string): string {
|
|
1524
|
+
const sanitized = sanitizeDiagnosticText(message);
|
|
1525
|
+
return sanitized.length > MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH
|
|
1526
|
+
? `${sanitized.slice(0, MAX_PROVIDER_ERROR_CAUSE_MESSAGE_LENGTH)}… [truncated]`
|
|
1527
|
+
: sanitized;
|
|
1528
|
+
}
|
|
1474
1529
|
|
|
1475
1530
|
function providerErrorCauseChain(error: unknown): ProviderErrorCauseFrame[] | undefined {
|
|
1476
1531
|
if (!(error instanceof Error) && !isProviderError(error)) return undefined;
|
|
@@ -1486,11 +1541,15 @@ function providerErrorCauseChain(error: unknown): ProviderErrorCauseFrame[] | un
|
|
|
1486
1541
|
) {
|
|
1487
1542
|
seen.add(cause);
|
|
1488
1543
|
const message = cause.message;
|
|
1544
|
+
const providerObservability = safeProviderErrorObservability(cause);
|
|
1545
|
+
const causeCode = providerErrorCode(cause);
|
|
1489
1546
|
frames.push({
|
|
1490
1547
|
errorClass: cause.name,
|
|
1491
|
-
...(
|
|
1548
|
+
...(causeCode !== undefined ? { code: causeCode } : {}),
|
|
1549
|
+
message: providerErrorCauseMessage(message),
|
|
1492
1550
|
messageLength: message.length,
|
|
1493
1551
|
messageFingerprint: createHash("sha256").update(message).digest("hex").slice(0, 12),
|
|
1552
|
+
...(providerObservability ? { providerObservability } : {}),
|
|
1494
1553
|
});
|
|
1495
1554
|
cause = cause.cause;
|
|
1496
1555
|
}
|
|
@@ -1507,11 +1566,13 @@ function logProviderError(
|
|
|
1507
1566
|
error: unknown,
|
|
1508
1567
|
status: number,
|
|
1509
1568
|
cost: ProviderRequestCost,
|
|
1510
|
-
declaredErrorCode
|
|
1511
|
-
proxyTelemetry
|
|
1569
|
+
declaredErrorCode: OperationErrorCode | undefined,
|
|
1570
|
+
proxyTelemetry: ProxyTelemetryCollector | undefined,
|
|
1571
|
+
observabilityDetails: ErrorObservabilityDetails,
|
|
1512
1572
|
): void {
|
|
1573
|
+
const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
|
|
1513
1574
|
const code = isProviderError(error)
|
|
1514
|
-
? (
|
|
1575
|
+
? (providerCode ?? "provider_error")
|
|
1515
1576
|
: error instanceof z.ZodError
|
|
1516
1577
|
? "invalid_request"
|
|
1517
1578
|
: error instanceof StatefulRoutingDeadlineError
|
|
@@ -1520,16 +1581,22 @@ function logProviderError(
|
|
|
1520
1581
|
const errorClass = error instanceof Error ? error.name : typeof error;
|
|
1521
1582
|
const message = error instanceof Error ? error.message : String(error);
|
|
1522
1583
|
const causeChain = providerErrorCauseChain(error);
|
|
1523
|
-
const details =
|
|
1584
|
+
const details = observabilityDetails;
|
|
1524
1585
|
const isUnregisteredProviderErrorCode =
|
|
1525
1586
|
status === 500 &&
|
|
1526
1587
|
isProviderError(error) &&
|
|
1527
1588
|
!isValidationError(error) &&
|
|
1528
|
-
typeof
|
|
1529
|
-
!SDK_OWNED_PROVIDER_ERROR_CODES.has(
|
|
1589
|
+
typeof providerCode === "string" &&
|
|
1590
|
+
!SDK_OWNED_PROVIDER_ERROR_CODES.has(providerCode) &&
|
|
1530
1591
|
declaredErrorCode === undefined;
|
|
1531
1592
|
const proxy = proxyTelemetry?.toLogPayload();
|
|
1532
1593
|
const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
|
|
1594
|
+
// The logger is caller-supplied and may mutate the event synchronously.
|
|
1595
|
+
// Give it an independent snapshot so those mutations cannot corrupt the
|
|
1596
|
+
// observability header serialized immediately afterwards.
|
|
1597
|
+
const providerObservability = details.providerObservability
|
|
1598
|
+
? { ...details.providerObservability }
|
|
1599
|
+
: undefined;
|
|
1533
1600
|
emit({
|
|
1534
1601
|
level: status >= 500 ? "error" : "warn",
|
|
1535
1602
|
event: "provider_request_failed",
|
|
@@ -1544,6 +1611,7 @@ function logProviderError(
|
|
|
1544
1611
|
errorClass,
|
|
1545
1612
|
message,
|
|
1546
1613
|
...(causeChain ? { causeChain } : {}),
|
|
1614
|
+
...(providerObservability ? { providerObservability } : {}),
|
|
1547
1615
|
...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
|
|
1548
1616
|
errorCategory: details.category,
|
|
1549
1617
|
taxonomyVersion: details.taxonomyVersion,
|
|
@@ -2375,7 +2443,11 @@ function createServerAppWithCapabilityModules(
|
|
|
2375
2443
|
|
|
2376
2444
|
app.notFound((c) => {
|
|
2377
2445
|
const error = new ProviderError("Not found", { code: "not_found", retryable: false });
|
|
2378
|
-
|
|
2446
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2447
|
+
return responseWithErrorObservability(
|
|
2448
|
+
c.json(toErrorResponse(error, undefined, observabilityDetails), 404),
|
|
2449
|
+
observabilityDetails,
|
|
2450
|
+
);
|
|
2379
2451
|
});
|
|
2380
2452
|
|
|
2381
2453
|
app.get("/health", (c) =>
|
|
@@ -2534,10 +2606,14 @@ function createServerAppWithCapabilityModules(
|
|
|
2534
2606
|
} catch (error) {
|
|
2535
2607
|
const declaredErrorCode = declaredErrorCodeFor(error, operationId, operationErrorCodes);
|
|
2536
2608
|
const status = toStatusCode(error, declaredErrorCode);
|
|
2537
|
-
if (
|
|
2609
|
+
if (
|
|
2610
|
+
isProviderError(error) &&
|
|
2611
|
+
providerErrorCode(error) === "STATEFUL_FORWARDING_REPLAY_CACHE_FULL"
|
|
2612
|
+
) {
|
|
2538
2613
|
c.header("Retry-After", String(STATEFUL_FORWARDING_REPLAY_RETRY_AFTER_SECONDS));
|
|
2539
2614
|
}
|
|
2540
2615
|
const requestId = extractRequestId(rawBody);
|
|
2616
|
+
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
2541
2617
|
logProviderError(
|
|
2542
2618
|
logger,
|
|
2543
2619
|
provider,
|
|
@@ -2548,11 +2624,12 @@ function createServerAppWithCapabilityModules(
|
|
|
2548
2624
|
status,
|
|
2549
2625
|
finishRequestCost(requestCost),
|
|
2550
2626
|
declaredErrorCode,
|
|
2627
|
+
undefined,
|
|
2628
|
+
observabilityDetails,
|
|
2551
2629
|
);
|
|
2552
2630
|
return responseWithErrorObservability(
|
|
2553
|
-
c.json(toErrorResponse(error, requestId,
|
|
2554
|
-
|
|
2555
|
-
declaredErrorCode,
|
|
2631
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
2632
|
+
observabilityDetails,
|
|
2556
2633
|
);
|
|
2557
2634
|
}
|
|
2558
2635
|
});
|
|
@@ -2609,6 +2686,7 @@ function createServerAppWithCapabilityModules(
|
|
|
2609
2686
|
const declaredErrorCode = declaredErrorCodeFor(error, operation, operationErrorCodes);
|
|
2610
2687
|
const status = toStatusCode(error, declaredErrorCode);
|
|
2611
2688
|
const requestId = extractRequestId(rawBody);
|
|
2689
|
+
const observabilityDetails = errorObservabilityDetails(error, declaredErrorCode);
|
|
2612
2690
|
logProviderError(
|
|
2613
2691
|
logger,
|
|
2614
2692
|
provider,
|
|
@@ -2620,13 +2698,13 @@ function createServerAppWithCapabilityModules(
|
|
|
2620
2698
|
finishRequestCost(requestCost),
|
|
2621
2699
|
declaredErrorCode,
|
|
2622
2700
|
proxyTelemetry,
|
|
2701
|
+
observabilityDetails,
|
|
2623
2702
|
);
|
|
2624
2703
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2625
2704
|
if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2626
2705
|
return responseWithErrorObservability(
|
|
2627
|
-
c.json(toErrorResponse(error, requestId,
|
|
2628
|
-
|
|
2629
|
-
declaredErrorCode,
|
|
2706
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
2707
|
+
observabilityDetails,
|
|
2630
2708
|
);
|
|
2631
2709
|
}
|
|
2632
2710
|
});
|
|
@@ -2668,6 +2746,7 @@ function createServerAppWithCapabilityModules(
|
|
|
2668
2746
|
} catch (error) {
|
|
2669
2747
|
const status = toStatusCode(error);
|
|
2670
2748
|
const requestId = extractRequestId(rawBody);
|
|
2749
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2671
2750
|
logProviderError(
|
|
2672
2751
|
logger,
|
|
2673
2752
|
provider,
|
|
@@ -2679,12 +2758,13 @@ function createServerAppWithCapabilityModules(
|
|
|
2679
2758
|
finishRequestCost(requestCost),
|
|
2680
2759
|
undefined,
|
|
2681
2760
|
proxyTelemetry,
|
|
2761
|
+
observabilityDetails,
|
|
2682
2762
|
);
|
|
2683
2763
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2684
2764
|
if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2685
2765
|
return responseWithErrorObservability(
|
|
2686
|
-
c.json(toErrorResponse(error, requestId), status),
|
|
2687
|
-
|
|
2766
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
2767
|
+
observabilityDetails,
|
|
2688
2768
|
);
|
|
2689
2769
|
}
|
|
2690
2770
|
});
|
|
@@ -2726,6 +2806,7 @@ function createServerAppWithCapabilityModules(
|
|
|
2726
2806
|
} catch (error) {
|
|
2727
2807
|
const status = toStatusCode(error);
|
|
2728
2808
|
const requestId = extractRequestId(rawBody);
|
|
2809
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2729
2810
|
logProviderError(
|
|
2730
2811
|
logger,
|
|
2731
2812
|
provider,
|
|
@@ -2737,12 +2818,13 @@ function createServerAppWithCapabilityModules(
|
|
|
2737
2818
|
finishRequestCost(requestCost),
|
|
2738
2819
|
undefined,
|
|
2739
2820
|
proxyTelemetry,
|
|
2821
|
+
observabilityDetails,
|
|
2740
2822
|
);
|
|
2741
2823
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2742
2824
|
if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2743
2825
|
return responseWithErrorObservability(
|
|
2744
|
-
c.json(toErrorResponse(error, requestId), status),
|
|
2745
|
-
|
|
2826
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
2827
|
+
observabilityDetails,
|
|
2746
2828
|
);
|
|
2747
2829
|
}
|
|
2748
2830
|
});
|
|
@@ -2784,6 +2866,7 @@ function createServerAppWithCapabilityModules(
|
|
|
2784
2866
|
} catch (error) {
|
|
2785
2867
|
const status = toStatusCode(error);
|
|
2786
2868
|
const requestId = extractRequestId(rawBody);
|
|
2869
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2787
2870
|
logProviderError(
|
|
2788
2871
|
logger,
|
|
2789
2872
|
provider,
|
|
@@ -2795,12 +2878,13 @@ function createServerAppWithCapabilityModules(
|
|
|
2795
2878
|
finishRequestCost(requestCost),
|
|
2796
2879
|
undefined,
|
|
2797
2880
|
proxyTelemetry,
|
|
2881
|
+
observabilityDetails,
|
|
2798
2882
|
);
|
|
2799
2883
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2800
2884
|
if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2801
2885
|
return responseWithErrorObservability(
|
|
2802
|
-
c.json(toErrorResponse(error, requestId), status),
|
|
2803
|
-
|
|
2886
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
2887
|
+
observabilityDetails,
|
|
2804
2888
|
);
|
|
2805
2889
|
}
|
|
2806
2890
|
});
|
|
@@ -2842,6 +2926,7 @@ function createServerAppWithCapabilityModules(
|
|
|
2842
2926
|
} catch (error) {
|
|
2843
2927
|
const status = toStatusCode(error);
|
|
2844
2928
|
const requestId = extractRequestId(rawBody);
|
|
2929
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2845
2930
|
logProviderError(
|
|
2846
2931
|
logger,
|
|
2847
2932
|
provider,
|
|
@@ -2853,12 +2938,13 @@ function createServerAppWithCapabilityModules(
|
|
|
2853
2938
|
finishRequestCost(requestCost),
|
|
2854
2939
|
undefined,
|
|
2855
2940
|
proxyTelemetry,
|
|
2941
|
+
observabilityDetails,
|
|
2856
2942
|
);
|
|
2857
2943
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2858
2944
|
if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2859
2945
|
return responseWithErrorObservability(
|
|
2860
|
-
c.json(toErrorResponse(error, requestId), status),
|
|
2861
|
-
|
|
2946
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
2947
|
+
observabilityDetails,
|
|
2862
2948
|
);
|
|
2863
2949
|
}
|
|
2864
2950
|
});
|
|
@@ -2900,6 +2986,7 @@ function createServerAppWithCapabilityModules(
|
|
|
2900
2986
|
} catch (error) {
|
|
2901
2987
|
const status = toStatusCode(error);
|
|
2902
2988
|
const requestId = extractRequestId(rawBody);
|
|
2989
|
+
const observabilityDetails = errorObservabilityDetails(error);
|
|
2903
2990
|
logProviderError(
|
|
2904
2991
|
logger,
|
|
2905
2992
|
provider,
|
|
@@ -2911,12 +2998,13 @@ function createServerAppWithCapabilityModules(
|
|
|
2911
2998
|
finishRequestCost(requestCost),
|
|
2912
2999
|
undefined,
|
|
2913
3000
|
proxyTelemetry,
|
|
3001
|
+
observabilityDetails,
|
|
2914
3002
|
);
|
|
2915
3003
|
const telemetryHeader = proxyTelemetry.toHeaderValue();
|
|
2916
3004
|
if (telemetryHeader) c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
|
|
2917
3005
|
return responseWithErrorObservability(
|
|
2918
|
-
c.json(toErrorResponse(error, requestId), status),
|
|
2919
|
-
|
|
3006
|
+
c.json(toErrorResponse(error, requestId, observabilityDetails), status),
|
|
3007
|
+
observabilityDetails,
|
|
2920
3008
|
);
|
|
2921
3009
|
}
|
|
2922
3010
|
});
|