A Karate 2.x mock builds its reply with a Background helper * def uuid = function(){ return java.util.UUID.randomUUID() + '' } copied from the mock documentation. The server starts, but every request to the scenario that uses it returns 500. Why?
answer
- a default that only mock mode has
- defining a function runs nothing yet
- the request body is untrusted input
- reaching into Java is switched off
basics
~20 sKarate 2.x turns Java interop off by default inside a mock feature. Defining the function touched no Java, so the server started; the first call evaluates java.util.UUID with no bridge behind it, the step fails, and the mock answers 500.
solid answer
~50 sMock mode in Karate 2.x runs with the Java bridge **disabled by default**, so `Java.type(...)` throws `java bridge not enabled` and a bare fully-qualified class reference does not resolve. Defining the helper in the `Background` touches no Java — a function body is not executed at definition — which is why the server came up clean and only the first request that calls `uuid()` fails. The default exists because a mock routinely assigns caller-controlled request data and then processes it for embedded expressions; with Java reachable, a `#(...)` inside a posted body was a remote code execution path. Its sibling flag `requestExpressionsEnabled` is off for the same reason. The fixes, best first: use `karate.uuid()`, which needs no bridge; or opt back in with `* configure javaBridgeEnabled = true` in the `Background` for a trusted mock.
code
gherkin · 9 linesFeature: cat store mock
Background:
# java.util.UUID would fail here on 2.x: no Java bridge in mock mode
* def newId = function(){ return karate.uuid() }
Scenario: pathMatches('/cats') && methodIs('post')
* def responseStatus = 201
* def response = { id: '#(newId())', name: '#(request.name)' }go deeper
Recall that a Karate 2.x mock cannot reach Java by default, and that karate.uuid() is the built-in way to get a random id into a mock response without it.
Explain the timing: defining a function runs nothing, so the failure appears on the first request that calls it rather than at server start-up — a distinction worth making in any lazy-evaluation question.
Know why the default exists. A mock assigns caller-controlled data and then processes it for embedded expressions, so Java interop turned an incoming body into an execution path; the two flags close the two halves independently.
Decide who may opt back in. A shared mock that accepts external traffic and re-enables the Java bridge is an internal service with an unauthenticated code path, and that call should not sit in one team's feature file unreviewed.
## Why the server started and the request still failed A `Background` step that defines a function does not run the function's body. `* def uuid = function(){ ... }` only creates the closure, so nothing touched Java at start-up and the mock came up clean. The first time a scenario actually *calls* `uuid()`, the expression `java.util.UUID.randomUUID()` is evaluated — and in a Karate 2.x mock there is no Java bridge behind it, so the evaluation throws and the request comes back **500**. The same recipe works unchanged in a Karate 2.x *test* feature. The restriction is specific to mock mode. ## What 2.x turns off, and why Two independent, default-on protections apply inside a mock feature, each with its own opt-out: | flag | default in a mock | what it governs | |---|---|---| | `javaBridgeEnabled` | off | whether `Java.type(...)` and bare fully-qualified Java class references resolve at all | | `requestExpressionsEnabled` | off | whether `#(...)` expressions found in **request-derived** data are evaluated | The reason they exist together is the attack they close. A mock routinely assigns data the caller controls — `* def body = request`, `bodyPath('$.x')`, `headerValue('X-Thing')` — and Karate then processes values for embedded expressions on the way into a response. With Java interop available, a body containing `#(Java.type('java.lang.Runtime')...)` would have been evaluated by the mock: the caller of a test double would have been executing code inside it. Turning request data inert closes one half; turning the Java bridge off closes the other, and each is enough on its own. The visible consequences in a 2.x mock: - `Java.type('java.util.UUID')` throws **`java bridge not enabled`**; - a bare `java.util.UUID.randomUUID()` fails to resolve, so the step that evaluates it fails; - a `#(...)` expression sitting in data that came from the request is echoed back **verbatim**, not evaluated — a mock that mirrors a posted body returns the literal `#(1 + 1)`, not `2`. ## The failure looks different depending on where the helper is called - Called directly — `* def id = uuid()` — the step itself fails and the caller gets a 500 carrying the error message. - Called from a whole-value embedded expression in the body — `* def response = { id: '#(uuid())' }` — the *step* survives, because an embedded expression that throws is deliberately left as its own source text. In an ordinary feature that is a feature: the match engine can re-resolve it later. In a mock it cannot, because nothing re-resolves a mock's response body — it goes on the wire as data. Karate 2.x guards that specific case and answers **500** naming the unevaluated placeholder rather than serving the literal string `#(uuid())` to every consumer. That second path is worth internalising. A stand-in quietly serving `"#(uuid())"` where an id belongs is the exact class of corruption a mock exists to prevent, and no test downstream would see it as anything but bad data. ## Three ways out, in order of preference 1. **Do not need Java.** For this specific case `karate.uuid()` is a built-in that returns a random UUID string and never touches the bridge. Most Java one-liners in mock helpers — a uuid, a timestamp, a random number — have a JavaScript or `karate.*` equivalent. 2. **Opt the bridge back in for a trusted mock**, with `* configure javaBridgeEnabled = true` in the `Background`. The setting is applied before the `Background` runs, which is exactly why the opt-in works from there. 3. **Opt in when you build the server**, via the mock server builder's flag, when the mock is started from Java rather than from a feature file. Reach for 2 or 3 only when the mock's callers are trusted. A mock that both accepts external input and has the Java bridge on is back to the shape the defaults were introduced to remove. ## The trap for anyone reading the documentation The `java.util.UUID` helper is the documented example of a dynamic mock response, and it is carried verbatim into the 2.x mock documentation from the 1.x guide it was written for. It is the single most likely way to meet this behaviour: the code is copied from a page that describes the product you are using, and it cannot work as printed under the current default. Treat a mock helper that reaches into Java as something to check against the version you are actually running, not as something the docs have already validated for you.
- A Karate 2.x mock echoes a posted body straight back. A client posts `{ "note": "#(1 + 1)" }`. What comes back?The literal string `#(1 + 1)`. Request-derived data is treated as inert by default, so its embedded expressions are never evaluated — the protection follows the value even after a step extracts it out of the request container. Opting in with `configure requestExpressionsEnabled = true` makes it evaluate to 2, and re-opens the injection surface the default closes.
- How do you turn Java interop back on for a mock that genuinely needs it?Either `* configure javaBridgeEnabled = true` in the `Background`, which works because the mock applies its default before the `Background` runs, or the equivalent flag on the mock server builder when the server is started from Java. Both are opt-ins for a trusted mock; a mock exposed to untrusted callers should stay on the default.
- Why does a helper that throws inside a `#(...)` in the response body still fail the request, when the step itself passed?An embedded expression that throws is deliberately left as its own source text, so the match engine can re-resolve it later. A mock response is never re-resolved — it goes on the wire as data — so Karate 2.x checks the finished body for placeholders whose expressions were recorded as failing and answers 500 naming one, rather than serving the literal `#(...)` text to a consumer.
saying these in an interview costs you the question
- Says you can always call Java from a Karate mock feature file.
- Blames the Background, without noticing a function body is not run at definition.
- Assumes an embedded expression that throws will fail its own step.
- Thinks a mock evaluates embedded expressions found in the posted body.
- Treats a documentation snippet as validated against the version in use.