In k6, what happens when an exported `options` object sets both `stages` and `scenarios`?
answer
- shorthand and the long form conflict
- one of duration, iterations, stages
- refused as invalid configuration, exit 104
- vus beside scenarios only warns
basics
~20 sk6 refuses to start. It reports that using stages and scenarios options simultaneously is not allowed and exits 104, invalid configuration, before any request is sent - because stages is shorthand that k6 would have to turn into a scenario itself.
solid answer
~40 sIt is a configuration error: k6 prints *using `stages` and `scenarios` options simultaneously is not allowed* and exits **104** before sending a request. The reason is that `duration`, `iterations` and `stages` are shorthand. Before the run starts, k6 converts whichever of them is present into exactly one entry in the `scenarios` map, keyed `default` - so supplying both asks it to derive a scenarios map you have already written. The same refusal covers `duration` with `stages`, `iterations` with `stages`, and either of those with `scenarios`. The one shorthand that is not exclusive is `vus`, which pairs with the others - but `vus` next to a `scenarios` map is worse than an error: k6 only warns and discards your scenarios.
code
javascript · 7 lines// rejected: k6 exits 104 before any request
export const options = {
stages: [{ duration: '1m', target: 20 }],
scenarios: { load: { executor: 'constant-vus' } },
};
export default function () {}go deeper
Remember the rule as a shape: pick one of duration, iterations or stages, or write scenarios and none of them. vus is the only key that pairs with the others.
Explain why they conflict - the shorthand keys are rewritten into a single default entry in the scenarios map before the run starts, so both cannot describe the same slot.
Know the asymmetry: stages beside scenarios exits 104, but vus beside scenarios only warns and discards the map. Catching that in review is worth more than knowing the error text.
Decide whether your team writes shorthand at all. Standardising on scenarios everywhere costs verbosity in small scripts but removes a whole class of silently-overridden configuration from the codebase.
## The refusal Put both in one object and the run never starts: ```javascript export const options = { stages: [{ duration: '1m', target: 20 }], scenarios: { load: { executor: 'constant-vus' } }, }; ``` k6 reports *using `stages` and `scenarios` options simultaneously is not allowed*, treats it as invalid configuration and exits **104**. Nothing is requested, no metrics are produced. This is one of the few things a k6 script can get wrong that fails loudly rather than quietly. ## Why the two cannot coexist `duration`, `iterations` and `stages` are not a second configuration system - they are **shorthand for a scenario**. Before the run starts, k6 rewrites whichever one it finds into a single entry in the `scenarios` map, always keyed `default`. Supplying a `scenarios` map as well asks it to write into a map you have already authored, so it refuses rather than guessing. What the shorthand derives: | root keys present | the derived `default` scenario runs on | |---|---| | `duration` (with optional `vus`) | the constant-VUs executor | | `iterations` (with optional `vus`) | the shared-iterations executor | | `stages` (with optional `vus`) | the ramping-VUs executor | | `vus` on its own | the shared-iterations executor | | none of them | the per-VU-iterations executor, 1 VU and 1 iteration | That last row is why `k6 run script.js` on a script with no `options` at all does something rather than nothing: it runs the default function once. ## The full set of refusals Five pairings are rejected outright, all with the same shape of message and the same exit code: - `iterations` with `stages` - `iterations` with `scenarios` - `duration` with `stages` - `duration` with `scenarios` - `stages` with `scenarios` Read that list as one rule rather than five: **pick at most one of `duration`, `iterations` and `stages`, and if you write `scenarios`, pick none of them.** The refusal happens while k6 is building the run's configuration, not while it is running, so it costs nothing and tells you exactly what to delete. Note also what is *not* on the list: `duration` with `iterations`. Those two are not rejected - `iterations` is checked first and wins, and a `duration` alongside it becomes a ceiling on how long the derived scenario is allowed to take rather than a second workload description. ## The exception that is not an error - and it is the dangerous one `vus` is the odd key out. It is not exclusive with the other shorthand; it feeds the derived scenario, which is why `vus` with `duration` and `vus` with `stages` are both ordinary, everyday configurations. But `vus` alongside a `scenarios` map does **not** fail. k6 logs a warning that `vus` overrides the scenarios configuration, throws your `scenarios` map away, and runs a derived default scenario instead. A leftover `vus: 10` at the top of an object whose real workload is a carefully written `scenarios` map means the map never runs, the exit code is 0, and the summary reports a run you did not ask for. So the two ways of getting this wrong fail in opposite directions: 1. `stages` next to `scenarios` - **exits 104**, obvious, fixed in a minute. 2. `vus` next to `scenarios` - **warns and runs the wrong workload**, which is discoverable only by reading the log or the derived configuration. ## Diagnosing it in practice - **Read the message, not the stack.** The refusal names both offending keys verbatim, so the fix is nearly always deleting one line. - **When migrating a script from shorthand to `scenarios`, delete the shorthand in the same edit.** Leaving `vus` behind is the single most common way to build a `scenarios` map that never executes. - **Confirm what ran.** `exec.test.options.scenarios` shows the derived configuration the run is using, so a script can prove whether its authored map survived or was replaced. - **Treat a run that ends far sooner or later than the scenarios map implies as this bug first.** A discarded map usually shows up as a wrong-length run, not as an error. - **Watch for it in generated or templated configuration.** Objects assembled by spreading a shared base into a per-suite override are the usual source of a stray shorthand key, because no single file shows the finished object. The lesson generalises past this one error: k6 rewrites the exported `options` object before it runs it, and the rewrite has rules. Knowing that `duration`, `iterations` and `stages` are shorthand for a `scenarios` entry - not settings that sit alongside one - is what turns both of these failures from puzzling into obvious.
- What does a k6 script with no exported `options` at all actually run?One iteration in one virtual user. With no shorthand and no `scenarios` map, k6 derives a `default` scenario on the per-VU-iterations executor whose defaults are 1 VU and 1 iteration. That is why running an unconfigured script exercises the default function exactly once rather than doing nothing.
- Why is `vus` allowed next to `duration` when `duration` is not allowed next to `stages`?`vus` is not a competing workload description - it is an input to whichever one k6 derives, so `vus` with `duration` produces a constant-VUs scenario at that size. `duration` and `stages` each describe a whole workload on their own, so k6 cannot merge them and refuses instead.
- How would you tell that a k6 run discarded your `scenarios` map?Check the log for the warning that `vus` overrides the scenarios configuration, and print `exec.test.options.scenarios` from the running script. If the map you authored is not there and a `default` entry is, the shorthand won and your workload never ran.
saying these in an interview costs you the question
- Says k6 merges stages into the scenarios map
- Expects stages beside scenarios to warn rather than fail
- Thinks vus beside scenarios is also a hard error
- Believes an unconfigured k6 script runs zero iterations
- Treats duration and stages as combinable to shape a run