Contributing to Undici
July 4, 2026 ยท View on GitHub
Guides
This is a collection of guides on how to run and update undici, and how to run different parts of the project.
Update llhttp
The HTTP parser used by undici is a WebAssembly build of llhttp.
While the project itself provides a way to compile targeting WebAssembly, at the moment we embed the sources
directly and compile the module in undici.
The deps/llhttp/include folder contains the C header files, while the deps/llhttp/src folder contains
the C source files needed to compile the module.
The lib/llhttp folder contains the .js transpiled assets required to implement a parser.
The following are the steps required to perform an update.
Clone the llhttp project
git clone git@github.com:nodejs/llhttp.git
cd llhttp
Checkout a llhttp release
git checkout <tag>
Install the llhttp dependencies
npm i
Run the wasm build script
This requires docker installed on your machine.
npm run build:wasm
Copy the sources to undici
cp build/wasm/*.js <your-path-to-undici>/lib/llhttp/
cp build/wasm/*.js.map <your-path-to-undici>/lib/llhttp/
cp build/wasm/*.d.ts <your-path-to-undici>/lib/llhttp/
cp src/native/api.c src/native/http.c build/c/llhttp.c <your-path-to-undici>/deps/llhttp/src/
cp src/native/api.h build/llhttp.h <your-path-to-undici>/deps/llhttp/include/
Build the WebAssembly module in undici
This requires docker installed on your machine.
cd <your-path-to-undici>
npm run build:wasm
Commit the contents of lib/llhttp
Create a commit which includes all of the updated files in lib/llhttp.
Update WPTs
undici runs a subset of the web-platform-tests.
Steps:
npm run test:wpt and node test/web-platform-tests/wpt-runner.mjs setup will initialize the WPT submodule automatically when it is missing.
If you want to prepare the checkout explicitly, run:
git submodule update --init --recursive
Run the tests
Run the tests to ensure that any new failures are marked as such.
Before running the tests for the first time, you must setup the testing environment.
cd test/web-platform-tests
node wpt-runner.mjs setup
To run all tests:
npm run test:wpt
To run a subset of tests:
cd test/web-platform-tests
node wpt-runner.mjs run [filter] [filterb]
To run a single file:
cd test/web-platform-tests
node wpt-runner.mjs run /path/to/test
Debugging
Verbose logging can be enabled by setting the NODE_DEBUG flag:
npx cross-env NODE_DEBUG=UNDICI_WPT node --run test:wpt
(npx cross-env can be omitted on Linux and Mac)
Lint
npm run lint
Test
npm run test
Coverage
npm run coverage
Issuing Releases
Release is automatic on commit to main which bumps the package.json version field. Use the "Create release PR" github action to generate a release PR.
Building for externally shared node builtins
If you are packaging undici for a distro, this might help if you would like to use
an unbundled version instead of bundling one in libnode.so.
To enable this, pass EXTERNAL_PATH=/path/to/global/node_modules/undici to build/wasm.js.
Pass this path with loader.js appended to --shared-builtin-undici/undici-path in Node.js's configure.py.
If building on a non-Alpine Linux distribution, you may need to also set the WASM_CC, WASM_CFLAGS, WASM_LDFLAGS and WASM_LDLIBS environment variables before running build/wasm.js.
Similarly, you can set the WASM_OPT environment variable to utilize your own wasm-opt optimizer.
Benchmarks
cd benchmarks && npm i && npm run bench
The benchmarks will be available at http://localhost:3042.
Documentation
cd docs && npm i && npm run serve
The documentation will be available at http://localhost:3000.
Reproduction
When reporting a bug, a high-quality reproduction helps maintainers diagnose and fix the issue quickly. Follow the guidelines below to create a minimal, standalone reproduction script.
What Makes a Good Reproduction
- Standalone: The script must be self-contained and run without external dependencies beyond
undiciand Node.js built-in modules. - Minimal: Strip everything unrelated to the bug โ no production configs, no frameworks, no unnecessary middleware.
- Reproducible: Run the script and confirm the bug occurs before submitting.
- Specific: Include the exact
undiciAPI call that triggers the issue (e.g.,Client,Pool,Agent,fetch,request,stream,pipeline). - Run with Node's test runner: Use
node --test(Node 20+) ornode test/your-repro.js.
Standalone Server Pattern
Use createServer from node:http (or node:https for TLS-related bugs) to run a local server inside the same script. This avoids external service dependencies and keeps the reproduction fully self-contained.
'use strict'
const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { Client } = require('undici')
// https://github.com/nodejs/undici/issues/XXXX
test('short description of the bug', { timeout: 60000 }, async (t) => {
t.plan(1)
// 1. Start a local HTTP server that reproduces the scenario
const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {
// Simulate the bug-triggering response
res.statusCode = 200
res.setHeader('content-length', 100)
res.end('hello'.repeat(20))
})
t.after(() => { server.closeAllConnections?.(); server.close() })
server.listen(0)
await once(server, 'listening')
// 2. Create the undici client pointing to the local server
const client = new Client(`http://localhost:${server.address().port}`)
t.after(() => client.close())
// 3. Perform the request that triggers the bug
const { body } = await client.request({ path: '/', method: 'GET' })
let responseBody = ''
for await (const chunk of body) {
responseBody += chunk
}
t.assert.strictEqual(responseBody, 'hello'.repeat(20))
})
Save the script as test/repro-XXXX.js and run it:
node --test test/repro-XXXX.js
Fetch-Based Reproduction
For bugs involving fetch, use the same standalone server pattern:
'use strict'
const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { fetch, Agent } = require('undici')
// https://github.com/nodejs/undici/issues/XXXX
test('short description of the fetch bug', { timeout: 60000 }, async (t) => {
t.plan(1)
const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {
res.statusCode = 200
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ ok: true }))
})
t.after(() => server.close())
server.listen(0)
await once(server, 'listening')
const agent = new Agent({ keepAliveTimeout: 1 })
t.after(() => agent.close())
const response = await fetch(`http://localhost:${server.address().port}/`, {
dispatcher: agent
})
await response.body.cancel()
t.assert.strictEqual(response.status, 200)
})
Using Pool or Agent
For bugs involving connection pooling or agent behavior:
'use strict'
const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { Pool } = require('undici')
// https://github.com/nodejs/undici/issues/XXXX
test('Pool bug reproduction', { timeout: 60000 }, async (t) => {
t.plan(1)
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
res.end('ok')
})
t.after(() => server.close())
server.listen(0)
await once(server, 'listening')
const pool = new Pool(`http://localhost:${server.address().port}`)
t.after(() => pool.close())
const data = await pool.request({ path: '/', method: 'GET' })
await data.body.dump()
t.assert.strictEqual(data.statusCode, 200)
})
Adding the Reproduction to the Repository
If you are submitting a bug fix via a pull request, include a test file test/issue-XXXX.js that reproduces the bug and passes with the fix.
Checklist for Submitting a Bug Report
- Write a standalone reproduction script.
- Run it locally to confirm the bug is reproducible.
- Attach the script (or a gist link) in the bug report.
- Include the exact undici version and Node.js version.
- Describe the expected vs. actual behavior.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
-
(a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or
-
(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or
-
(c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.
-
(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
Moderation Policy
The Node.js Moderation Policy applies to this project.