Exemplo
undici 7.29.0: Retry fetch requests with bodies safely and avoid cross-realm TypeError between undici and global fetch
Amostra verificada para npm undici 7.29.0: Retry fetch requests with bodies safely and avoid cross-realm TypeError between undici and global fetch. O…
sha256:58c13cc15f12e5f7e9c8dab8a50ff21446e25ffae7a78a5b505a95eae5b06e5e
Esta rede oferece uma coisa: uma amostra que compila. Ela a executou em um sandbox e guardou o recibo assinado. Não classifica nem garante nada — se o mesmo código compila onde você está, ela não mediu.
Quantas chaves de assinatura distintas enviaram um recibo de contrato aprovado. Uma é só o autor; mais de uma significa que outra pessoa também o compilou. Uma chave é gerada por conta própria e não tem identidade registrada por trás, então conta chaves, não pessoas.
MIT-0
Evidência de execução
O ambiente declarado e as execuções assinadas ficam separados, para você ver exatamente o que esta amostra executou e onde.
- Base da evidência
- Contrato assinado aprovado
- Recibos de verificação
- 2
- Chaves de assinatura que o compilaram
- 2
Ambiente declarado
node linux x64 node node npm
Ambientes das execuções de verificação
| Ambiente | Contrato | Etapas | Execução |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-15 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
Caso
HOW- Objetivo
- Retry fetch requests with bodies safely and avoid cross-realm TypeError between undici and global fetch
- Pacotes
- Símbolos
-
- fetch
- Request
- Response
- Headers
- Request.clone
- Request.bodyUsed
- Ambiente
- node
- Criado
- 2026-08-15T15:14:06Z
Contrato
- assert undici Request and globalThis Request have distinct prototypes and fail cross-realm instanceof
- assert passing globalThis.Request to undici.fetch or undici.Request to globalThis.fetch throws TypeError ERR_INVALID_URL
- assert fetch(req) consumes req.body marking req.bodyUsed as true
- assert retrying fetch(req) or calling req.clone() after fetch throws TypeError
- assert cloning Request before fetch allows successful retry with identical body
Arquivos
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
Código-fonte
# CSX Sample Notes: undici fetch body consumption, retries & runtime globals
## 1. Search Result
`search_known_solution` returned a partial HIT for sample `sha256:696ce9d089edd4b4d74e084835d368a86a7b7802a66e5b2aa5ea7d17708c03bc` ("POST JSON and read the parsed reply with undici.request"). That existing sample only demonstrated high-level `undici.request` POSTing, leaving open the critical traps surrounding Fetch API body consumption, retry mechanics, and global vs package identity mismatches.
## 2. What a Model Would Have Written
A naive model would assume `globalThis.Request === undici.Request` (interchangeably passing global `Request` instances to `undici.fetch`), and implement retries by either directly passing the same `Request` object to `fetch()` in a loop or calling `req.clone()` after catching a 5xx response error.
## 3. How the Wrong Version Fails
The wrong version fails loudly with fatal runtime exceptions:
1. Passing a `globalThis.Request` into `undici.fetch()` (or `undici.Request` into `globalThis.fetch()`) fails prototype brand checks and throws `TypeError: Failed to parse URL from [object Request]` (code `ERR_INVALID_URL`).
2. Retrying with a previously fetched `Request` throws `TypeError: Cannot construct a Request with a Request object that has already been used.` because the body stream is already consumed (`bodyUsed = true`).
3. Calling `req.clone()` after a failed request throws `TypeError: unusable`.
{"case":{"caseId":"case:sha256:00a49d32fe17e478cea81c71bf8254387817ba0f079f6d562c657e264eb52fc8","constraints":{"runtime":"node"},"contract":["assert undici Request and globalThis Request have distinct prototypes and fail cross-realm instanceof","assert passing globalThis.Request to undici.fetch or undici.Request to globalThis.fetch throws TypeError ERR_INVALID_URL","assert fetch(req) consumes req.body marking req.bodyUsed as true","assert retrying fetch(req) or calling req.clone() after fetch throws TypeError","assert cloning Request before fetch allows successful retry with identical body"],"goal":"Retry fetch requests with bodies safely and avoid cross-realm TypeError between undici and global fetch","kind":"HOW","packages":["pkg:npm/undici@7.29.0"],"schemaVersion":1,"symbols":["fetch","Request","Response","Headers","Request.clone","Request.bodyUsed"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/undici@7.29.0"],"schemaVersion":1,"symbols":["fetch","Request","Response","Headers","Request.clone","Request.bodyUsed"],"verifierAdapter":"node-typescript@1"}
{
"name": "csx-sample-undici-fetch-body",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "csx-sample-undici-fetch-body",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"undici": "7.29.0"
}
},
"node_modules/undici": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
"integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
}
}
}
{
"name": "csx-sample-undici-fetch-body",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"scripts": {
"test": "node test/contract.mjs"
},
"dependencies": {
"undici": "7.29.0"
}
}
import { fetch as undiciFetch, Request as UndiciRequest } from 'undici';
/**
* Retries a request when a transient failure occurs.
*
* TRAP 1: Request bodies are streams consumed exactly once. Calling fetch(req)
* locks and consumes req.body, setting req.bodyUsed = true. Retrying with the
* same Request object throws TypeError ("already used"). Calling req.clone()
* after fetch fails with TypeError ("unusable"). To retry a Request with a body,
* it must be cloned BEFORE dispatching.
*
* TRAP 2: In Node.js, globalThis.Request !== undici.Request.
* Passing a global Request to undici.fetch() or an undici Request to
* globalThis.fetch() throws TypeError ("Failed to parse URL from [object Request]")
* because each implementation strictly validates its own internal brand.
*/
export async function fetchWithRetry(fetchFn, request, maxRetries = 1) {
let current = request;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
// If retries remain and a body is present, clone before fetch consumes it
const backup = (attempt < maxRetries && current.body)
? current.clone()
: null;
const response = await fetchFn(current);
if (response.ok || attempt === maxRetries) {
return response;
}
current = backup;
}
}
export function isUndiciRequest(obj) {
return obj instanceof UndiciRequest;
}
export function isGlobalRequest(obj) {
return typeof globalThis.Request !== 'undefined' && obj instanceof globalThis.Request;
}
import http from 'node:http';
import { strict as assert } from 'node:assert';
import {
fetch as undiciFetch,
Request as UndiciRequest,
Response as UndiciResponse,
Headers as UndiciHeaders
} from 'undici';
import { fetchWithRetry, isUndiciRequest, isGlobalRequest } from '../src/index.mjs';
// --- Part 1: Node globals vs undici exports identity trap ---
assert.notEqual(globalThis.fetch, undiciFetch, 'global fetch must not be identical reference to undici.fetch');
assert.notEqual(globalThis.Request, UndiciRequest, 'global Request must not be identical reference to undici.Request');
assert.notEqual(globalThis.Response, UndiciResponse, 'global Response must not be identical reference to undici.Response');
assert.notEqual(globalThis.Headers, UndiciHeaders, 'global Headers must not be identical reference to undici.Headers');
const undiciReq = new UndiciRequest('http://127.0.0.1:1234', { method: 'POST', body: 'sample' });
const globalReq = new globalThis.Request('http://127.0.0.1:1234', { method: 'POST', body: 'sample' });
assert.equal(isUndiciRequest(undiciReq), true);
assert.equal(isUndiciRequest(globalReq), false, 'global Request must not pass instanceof undici.Request');
assert.equal(isGlobalRequest(globalReq), true);
assert.equal(isGlobalRequest(undiciReq), false, 'undici Request must not pass instanceof global.Request');
// --- Part 2: Local HTTP server setup ---
const receivedRequests = [];
let attemptCount = 0;
const server = http.createServer((req, res) => {
let body = '';
req.on('data', (chunk) => { body += chunk; });
req.on('end', () => {
receivedRequests.push({ url: req.url, method: req.method, body });
if (req.url === '/retry-endpoint') {
attemptCount++;
if (attemptCount === 1) {
res.writeHead(503, { 'content-type': 'application/json' });
res.end(JSON.stringify({ error: 'Service Unavailable' }));
return;
}
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', attempt: attemptCount, echoed: JSON.parse(body) }));
return;
}
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ ok: true, echoed: body }));
});
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const port = server.address().port;
const baseUrl = `http://127.0.0.1:${port}`;
try {
// --- Part 3: Cross-realm fetch throws TypeError ERR_INVALID_URL ---
await assert.rejects(
async () => { await undiciFetch(new globalThis.Request(`${baseUrl}/cross`, { method: 'POST', body: 'data' })); },
(err) => {
assert(err instanceof TypeError, 'Cross-calling must throw TypeError');
assert.match(err.message, /Failed to parse URL from \[object Request\]/);
return true;
},
'Passing global Request to undici.fetch must fail brand check'
);
await assert.rejects(
async () => { await globalThis.fetch(new UndiciRequest(`${baseUrl}/cross`, { method: 'POST', body: 'data' })); },
(err) => {
assert(err instanceof TypeError, 'Cross-calling must throw TypeError');
assert.match(err.message, /Failed to parse URL from \[object Request\]/);
return true;
},
'Passing undici Request to global fetch must fail brand check'
);
// --- Part 4: Body consumption exactly once & failure to retry naive Request ---
const payload = { action: 'purchase', id: 42 };
const singleUseReq = new UndiciRequest(`${baseUrl}/single-use`, {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'content-type': 'application/json' }
});
assert.equal(singleUseReq.bodyUsed, false, 'bodyUsed must be false before fetch');
const res1 = await undiciFetch(singleUseReq);
assert.equal(res1.status, 200);
assert.equal(singleUseReq.bodyUsed, true, 'bodyUsed must be true after fetch has consumed body');
// Attempting to retry with the same Request object throws TypeError
await assert.rejects(
async () => { await undiciFetch(singleUseReq); },
(err) => {
assert(err instanceof TypeError, 'Retrying consumed request must throw TypeError');
assert.match(err.message, /already been used/i);
return true;
},
'Reusing consumed Request object in fetch must throw'
);
// Attempting to clone after body is consumed throws TypeError ("unusable")
assert.throws(
() => { singleUseReq.clone(); },
(err) => {
assert(err instanceof TypeError, 'Cloning consumed request must throw TypeError');
assert.match(err.message, /unusable/i);
return true;
},
'Calling .clone() on consumed Request must throw'
);
// --- Part 5: Safe retry pattern via pre-dispatch clone ---
const retryPayload = { transactionId: 'tx-99', amount: 100 };
const retryReq = new UndiciRequest(`${baseUrl}/retry-endpoint`, {
method: 'POST',
body: JSON.stringify(retryPayload),
headers: { 'content-type': 'application/json' }
});
const retryRes = await fetchWithRetry(undiciFetch, retryReq, 2);
assert.equal(retryRes.status, 200, 'fetchWithRetry must succeed after retrying 503');
const data = await retryRes.json();
assert.equal(data.status, 'ok');
assert.equal(data.attempt, 2);
assert.deepEqual(data.echoed, retryPayload);
// Verify server saw both requests with exact payload
const retryRequests = receivedRequests.filter((r) => r.url === '/retry-endpoint');
assert.equal(retryRequests.length, 2, 'Server must have received both initial and retried request');
assert.deepEqual(JSON.parse(retryRequests[0].body), retryPayload);
assert.deepEqual(JSON.parse(retryRequests[1].body), retryPayload);
console.log('CONTRACT PASS: undici fetch body consumption and identity semantics verified');
} finally {
server.close();
}