What does k6's `handleSummaryTimeout` option control, and how do you set it?
answer
- a budget for the summary hook
- two minutes unless you change it
- script option or environment variable
- no command-line flag exists
- expiry means no report at all
basics
~20 sIt caps how long k6 lets the handleSummary() call run before interrupting it, and the default is 120s. Set it in the script's exported options object or with the K6_HANDLE_SUMMARY_TIMEOUT environment variable; there is no command-line flag.
solid answer
~40 s`handleSummaryTimeout` is a root key of the exported `options` object that bounds the whole `handleSummary()` call — including any asynchronous work it awaits, because k6 runs the hook on a real event loop in a dedicated VU. Its default is `120s`. It can also be supplied as `K6_HANDLE_SUMMARY_TIMEOUT`, but unlike most summary settings it has **no CLI flag**. When the budget expires k6 interrupts the call and reports `handleSummary() execution timed out after N seconds`, with a hint naming the option and the environment variable. Nothing the hook was about to return is written, so a timeout costs you the whole report — not a partial one.
code
javascript · 9 linesexport const options = {
handleSummaryTimeout: '30s',
};
export default function () {}
export function handleSummary(data) {
return { 'junit.xml': '<testsuites/>' };
}go deeper
Know that the hook has a time budget of two minutes by default and that it is set as a key in the script's exported options object.
Explain that the budget covers awaited work as well, that the environment variable is the only alternative to the script option, and that expiry loses the whole report.
Argue for a deliberate value rather than the default: a report that should be instant deserves a short budget so a hung call fails quickly instead of stalling every run.
Treat the number as policy. It ships inside the script, so whatever value a shared template carries becomes the stall every team inherits when a summary hook hangs.
## What the clock covers k6 runs `handleSummary()` in a VU it creates specifically for the summary, with a context whose deadline is `handleSummaryTimeout`. Everything inside that boundary counts: - the synchronous body of your function; - anything you `await`, because the hook may be `async` and k6 unwraps the promise it returns; - timers and other event-loop work scheduled from the hook, which k6 drains before taking the result; - the init context of that fresh VU, which is executed again when the summary VU is created. The deadline is not a soft warning. When it passes, k6 interrupts the runtime and throws away whatever the call was going to produce, so the run ends with **no report at all** rather than a truncated one. ## How it is configured | knob | in the exported `options` object | environment variable | CLI flag | |---|---|---|---| | `handleSummaryTimeout` | yes | `K6_HANDLE_SUMMARY_TIMEOUT` | none | | summary mode | no | `K6_SUMMARY_MODE` | `--summary-mode` | That asymmetry is worth memorising, because the two settings that govern this hook are configured from opposite ends. The timeout is script-side and travels with the code; the summary mode is run-side and can be changed by whoever invokes k6. ```javascript export const options = { handleSummaryTimeout: '30s', }; export function handleSummary(data) { return { 'junit.xml': '<testsuites/>' }; } ``` ## What expiry looks like The error message is exact and easy to grep for: ``` handleSummary() execution timed out after 30 seconds ``` k6 attaches a hint alongside it — *you can increase the time limit via the handleSummaryTimeout option or the K6_HANDLE_SUMMARY_TIMEOUT environment variable* — and then logs `failed to handle the end-of-test summary`. As with every other failure in this path, the process's exit code is untouched, so a timed-out report does not turn a passing run red. ## Why a summary hook can be slow 1. **It talks to something.** The hook has the full k6 module surface available, so scripts sometimes upload the report over HTTP from inside it. A slow or hanging endpoint burns the budget directly. 2. **It renders something large.** Serialising and formatting a big summary object, especially into a verbose text format, is real CPU work in the script runtime. 3. **It awaits.** An `async` hook that never resolves consumes the entire allowance before k6 gives up. Lowering the value is as legitimate as raising it: a short budget on a report that should take milliseconds turns a hung upload into a fast, visible failure instead of two extra minutes at the end of every pipeline run. ## Choosing a value 1. **Measure what the hook actually costs.** A hook that formats the summary object and returns a string finishes in milliseconds; the two-minute allowance exists for the calls that do considerably more. 2. **Raise it when the hook does real work** — a large render, or a transfer to a service that is slow but dependable. k6 imposes no upper bound on the duration you set. 3. **Lower it when the hook should be instant.** A short budget converts a hung call into a fast, obvious failure instead of two extra minutes at the end of every run in the pipeline. 4. **Remember where the value lives.** It is a script option, so a value baked into a shared script becomes every run's behaviour; the environment variable is the per-invocation escape hatch. ## What the timeout does not do - It does not bound the test itself. By the time the hook is called, every iteration has already finished, so the budget only ever extends the closing seconds of a run. - It does not produce a partial artifact. The whole return value is lost, not truncated. - It does not fail the run. Like every other error in this path, a timed-out summary is logged and the process's exit status is left exactly as it was. ## Version notes In k6 v2 the value is a duration string such as `'30s'` or `'2m'`, the default is 120 seconds, and the option is one of the root keys of the exported `options` object rather than something you can pass on the command line. Because it is a script option, an archive or a shared script carries its own timeout with it wherever it runs.
- Does an `async` `handleSummary()` count against `handleSummaryTimeout`?Yes. k6 runs the hook on an event loop, drains scheduled work and unwraps a returned promise, and the whole call sits inside the timeout's context. An awaited request that never resolves consumes the entire budget.
- If the timeout fires, do you get a partially written file?No. k6 only sees a value once the call has returned, so an interrupted hook yields nothing to write. You get the timeout error in the log and no summary output of any kind.
saying these in an interview costs you the question
- Thinks handleSummaryTimeout has a --handle-summary-timeout flag
- Confuses it with the setup or teardown timeouts
- Believes a timeout writes whatever was ready so far
- Assumes async work in the hook is exempt from the budget
- Thinks a timed-out summary makes the run fail