In a Karate feature file, what is the difference between the step `* configure headers = { 'X-Trace': 'abc' }` and the step `* header X-Trace = 'abc'`?
answer
- one is per call, one is ambient
- what survives the request builder reset
- url is kept, headers are cleared
- configured headers merge in last
basics
~20 sconfigure headers sets a default header map that Karate re-applies to every later request in scope. The header step sets one header on the request being built, and Karate clears it as soon as that call completes.
solid answer
~40 sBoth write HTTP headers, but onto different objects. `* header X-Trace = 'abc'` writes onto the request builder, and Karate resets that builder after every `method` call — the reset clears `method`, `path`, `params`, `headers`, `body`, `cookies` and `retry until`, keeping only `url`. So a `header` step covers exactly one call. `* configure headers = { 'X-Trace': 'abc' }` writes onto the scenario's config object instead, and Karate re-reads it just before every request for as long as that config is in scope. The configured map is merged into the request **last**, so when both set the same name the configured value wins. The configured value may also be a JavaScript function, which Karate calls once per request and hands the request built so far.
code
gherkin · 16 linesFeature: header scope
Scenario: per-call versus ambient
* configure headers = { 'X-Trace': 'standing' }
* url 'https://api.test'
# this call carries X-Trace: standing
* path 'orders'
* method get
# path and any header step are gone now; url and the configured header remain
* header X-One-Off = 'just this call'
* path 'orders', 1
* method get
# this call carries X-One-Off AND X-Trace: standing
* path 'orders', 2
* method get
# X-One-Off is gone; X-Trace: standing is still appliedgo deeper
Remember the one-liner: a header step is for this call, a configure headers step is for every call. If a header vanished on the second request, you used the per-call form.
Explain the mechanism, not the slogan. The request builder is reset after every method call and keeps only the url; the config object is not reset and is re-read before each request.
Watch for the clash rule in review. Configured headers are merged last and overwrite a step-level header of the same name, so a per-call override that looks obvious can be silently discarded.
Decide as a team where ambient headers are allowed to live. Headers configured far from the call are invisible at the point of failure, so trade the reuse against how hard the resulting request is to reconstruct from one feature file.
## Two different objects A Karate scenario carries two mutable things that both know about headers, and the whole answer falls out of telling them apart. - The **request builder** holds the call being assembled right now: `url`, `path`, `param`, `header`, `cookie`, the `request` body, `retry until`. - The **config object** holds ambient settings Karate re-applies to every request it sends: `configure headers`, `configure cookies`, `configure ssl`, the timeouts, the retry defaults. `* header X-Trace = 'abc'` writes to the first. `* configure headers = { 'X-Trace': 'abc' }` writes to the second. ## The reset is what makes them behave differently When a `method` step finishes, Karate resets the request builder. The reset clears `method`, the collected paths, the params, the headers, the multipart parts, the body, the cookies and the `retry until` condition — and it deliberately keeps `url`. Karate's own source marks the exception with the comment `// url will be retained` on the first line of that method. | written by | lives on | survives the next `method` call? | |---|---|---| | `url 'https://api.test'` | request builder | yes, deliberately retained | | `path 'orders', id` | request builder | no, cleared | | `header X-Trace = 'abc'` | request builder | no, cleared | | `headers { 'X-Trace': 'abc' }` | request builder | no, cleared | | `configure headers = { ... }` | config object | yes, re-applied every request | That table answers most "why did my header disappear on the second call" questions. A `header` step is a per-call instruction; a `configure headers` step is a standing one. ## The near-identical step that is not it Karate also has a plural `headers` step that takes a whole map: `* headers { 'X-Trace': 'abc' }`. It reads almost the same as `configure headers = { 'X-Trace': 'abc' }` and behaves completely differently — no `configure` prefix, no `=`, and it lands on the request builder, so it is wiped by the same reset. The presence or absence of the word `configure` is the entire difference. This is a common misreading in review, because the two lines differ by one word and one equals sign. ## The configured map is applied last Just before each request goes out, Karate reads the `configure headers` value off the config and merges the resulting map into the request builder. That merge happens **after** every `header` step in the scenario has already run, and the merge is a case-insensitive put, so a configured header **overwrites** a step-level header of the same name for that request. If you want a one-off override of a configured header, change the config for that scenario rather than adding a `header` step and expecting it to win. ## The configured value can be a function `configure headers` accepts either a map or a JavaScript function. When it is a function, Karate calls it once per request and passes it the request as built so far, so the function can compute a value from the method, the URL or the body: 1. a fresh correlation id per request 2. a signature computed over the outgoing path and body 3. a header that only appears when some variable happens to be set The function is usually kept in its own `.js` file and pulled in with `read('classpath:headers.js')`, which is how one set of ambient headers gets shared across many features. If the function returns anything that is not a map, Karate adds nothing and raises no error — so a function with a missing `return` presents as "my headers silently stopped working". ## Choosing between them - Use `header` (or `headers`) for something that belongs to **one** call: an `If-None-Match` you are testing, a deliberately malformed value, a single-request override. - Use `configure headers` for something every call in the scope should carry: a tenant id, a content-negotiation default, a computed per-request header. - Prefer a literal map over the function form unless the value genuinely changes per request — a map is easier to read and costs no function call per request. ## What a strong answer sounds like "`header` writes onto the request Karate is building, and that builder is reset after every call, keeping only the `url`. `configure headers` writes onto the config instead, and Karate re-applies it to every request until something changes it. One is scoped to a call; the other is scoped to wherever you put the `configure` step."
- In Karate, which parts of a request survive after a `method` step and which are cleared?Only `url` survives, and the source says so explicitly. The reset clears the method, the accumulated `path` segments, `params`, step-level `headers`, multipart parts, the `request` body, `cookies` and the `retry until` condition. That is why a base URL is normally set once in a `Background` while `path` is re-stated before every call — and why a `retry until` has to be repeated for each call it should guard.
- What happens if the function assigned to `configure headers` returns something that is not a map?Nothing is added and nothing is reported. Karate evaluates the function, checks whether the result is a map, and discards it otherwise — there is no warning on that path. A JavaScript function that falls off the end without a `return`, or returns a string, therefore behaves exactly like no configured headers at all, which is why the symptom is usually reported as a 401 rather than as a config error.
- If a `header` step and `configure headers` both set `Content-Type`, which one reaches the wire?The configured one. Karate merges the configured map into the request builder immediately before sending, after every step has run, and the merge matches existing names case-insensitively and replaces them. So the standing instruction beats the per-call one on a name clash — the opposite of what most people guess. To override for a single call, change the config in that scenario instead of layering a `header` step on top.
A header step is a sticky note on one envelope. configure headers is the rubber stamp the mail room presses onto every envelope until someone changes the stamp.
saying these in an interview costs you the question
- Says a header step persists for the rest of the scenario
- Thinks configure headers only affects the next request
- Claims a step-level header always beats a configured one
- Reads a plain headers step as the same thing as a configure headers one
- Believes url is cleared after a call just like path is
- Assumes configure headers must be a literal map, never a function