In a k6 script, what do the keys of the `options.scenarios` object mean, and what must each entry declare?
answer
- the key is not decoration
- map, not array
- one key has no default
- letters, digits, underscore, dash only
basics
~20 sEvery key in k6's options.scenarios object is that scenario's name, unique within the script and limited to letters, digits, underscores and dashes. Each entry must declare executor; exec, startTime, gracefulStop, env and tags all have defaults.
solid answer
~40 sIn k6 v2, `options.scenarios` is a JavaScript object rather than an array, and the property name on the left is the scenario's identity — there is no `name` key inside the entry. k6 validates that name against `^[0-9a-zA-Z_-]+$`, so `checkout_v2` and `smoke-1` are fine while `checkout flow` and `checkout.v2` are rejected, and it reuses the name in the run header, in the per-scenario summary section, and as the value of the `scenario` tag. The only required key inside an entry is `executor`, naming one of k6 v2's six executors; omit it and k6 fails with `scenario '<name>' doesn't have a specified executor type` and exits 104 before sending a request. Everything else that is shared across executors has a default: `startTime` `0s`, `gracefulStop` `30s`, `exec` `"default"`, `env` and `tags` empty.
code
javascript · 25 linesimport http from 'k6/http';
export const options = {
scenarios: {
browse_catalog: {
executor: 'constant-vus',
vus: 20,
duration: '30s',
},
checkout: {
executor: 'per-vu-iterations',
exec: 'checkout',
vus: 5,
iterations: 10,
},
},
};
export default function () {
http.get('https://quickpizza.grafana.com/');
}
export function checkout() {
http.get('https://quickpizza.grafana.com/api/tools');
}go deeper
Recall that options.scenarios is a map, the key is the scenario name, and every entry must set executor. Knowing the default of exec is "default" is enough at this stage.
Explain what the name is reused for — header lines, the per-scenario summary section, the scenario tag, the (startTime, name) sort order — and name the shared keys with their defaults.
Show that a bad name or a missing executor is caught at config time and exits 104 before traffic, and that entries are decoded strictly so a stray key fails rather than being ignored.
Weigh scenario names as a public interface: they land in dashboards and tag filters, the charset is restricted, and renaming one silently breaks every saved query built on the scenario tag.
## `scenarios` is a map, and the key is the name In k6 v2 a script configures its workloads by exporting an `options` object with a `scenarios` property. That property is a plain JavaScript **object** (a map), not an array. Each property of that object is one **scenario** — one independently scheduled workload — and **the property name is the scenario's name**. There is no `name` field inside an entry to set; k6 fills the name in from the map key while it parses the map. Because it is a map rather than a list, uniqueness is free: writing the same key twice simply leaves the last one. Every scenario in the map runs inside the same `k6 run` process, on the same script, under the same root-level `options`. ```javascript export const options = { scenarios: { browse_catalog: { executor: 'constant-vus', vus: 20, duration: '1m' }, checkout: { executor: 'per-vu-iterations', exec: 'checkout', vus: 5, iterations: 10 }, }, }; ``` ## What k6 does with the name The key is not decoration — k6 threads it through the whole run: - the run header prints one line per scenario, `* <name>: <description>`; - the end-of-test summary gets a `SCENARIO: <name>` section per scenario; - k6 attaches a `scenario` tag whose value is the name to the samples that scenario emits; - k6 sorts scenarios by `(startTime, name)`, so the name is the tie-break when two scenarios start together; - configuration errors are reported per name: `scenario <name> has configuration errors: ...`. Because the name travels into output and tags, k6 restricts it. The name is matched against the regular expression `^[0-9a-zA-Z_-]+$` — digits, latin letters, underscores and dashes only. A name with a space, a dot, a slash or an accented character fails validation with *the scenario name should contain only numbers, latin letters, underscores, and dashes*. ## The one required key Every entry must declare `executor`, a string naming which of k6 v2's six executors schedules that scenario: `shared-iterations`, `per-vu-iterations`, `constant-vus`, `ramping-vus`, `constant-arrival-rate` or `ramping-arrival-rate`. Two distinct failures exist here, and both are configuration errors that stop the run with exit code **104** (`InvalidConfig`) before any traffic is generated: 1. the key is missing entirely — `scenario '<name>' doesn't have a specified executor type`; 2. the key names something k6 does not know — `unknown executor type '<value>'`. In k6 v2 that includes `externally-controlled`, which the v2.0.0 release removed. ## The shared keys every entry accepts Beyond `executor`, k6 defines a common block of keys that every executor accepts, whichever one you named: | key | type | default | what it does | |---|---|---|---| | `executor` | string | — (required) | which of the six executors schedules this scenario | | `startTime` | duration string | `"0s"` | offset from the start of the run at which this scenario begins | | `gracefulStop` | duration string | `"30s"` | how long in-flight iterations may run past the scenario's end | | `exec` | string | `"default"` | the exported function this scenario's VUs call | | `env` | object | `{}` | environment variables overlaid on `__ENV` for this scenario only | | `tags` | object | `{}` | tags added to the samples this scenario emits | | `options` | object | `{}` | scenario-scoped extras, currently browser options | Everything else in an entry belongs to the executor you named, and each executor defines a different set. ## Entries are parsed strictly k6 does not merge an entry loosely into a struct. It reads the map, looks at each entry's `executor` string, and hands that entry's raw JSON to the matching executor's config constructor, which decodes with `DisallowUnknownFields`. The practical consequence is that a key which is neither one of the shared keys above nor one of the named executor's own keys is a **hard configuration error**, not a silently ignored field. ## Common misreadings - **"`scenarios` is an array of objects with names inside."** It is a map; the key is the name. - **"Any string works as a scenario name."** Spaces and dots fail the name regex. - **"`executor` is optional and defaults to something."** It is the one key with no default. - **"An entry with a stray key just ignores it."** Inside a scenario entry it fails the run. - **"Each scenario is a separate k6 run."** They all run in one process and one summary.
- What does k6 do if two scenarios in the map name the same `exec` function?Nothing special — that is legal and common. Both scenarios call the same exported function, each with its own executor, `startTime`, `env` and `tags`, and k6 keeps their samples apart through the `scenario` tag and the per-scenario summary sections. `exec` defaults to `"default"`, so scenarios that omit it all share the default function.
- Where does k6 get the scenario name from when the script never writes `scenarios` at all?It invents one. When a script sets only root-level shortcut options such as `vus` and `duration`, k6 derives a single scenario named `default` from them, so the header and the `scenario` tag both read `default`. That name is the constant `DefaultScenarioName` in k6, and it is why so many summaries show a scenario nobody wrote.
saying these in an interview costs you the question
- Calling options.scenarios an array of scenario objects
- Expecting a name field inside the entry instead of the map key
- Assuming executor has a default and can be omitted
- Using a scenario name with spaces or dots
- Believing each scenario runs as its own k6 process