Why does REST Assured's matchesJsonSchemaInClasspath throw IllegalArgumentException: Schema to use cannot be null?
answer
- getResource returned null
- no leading slash, classpath root
- must be on the test classpath
- thrown before anything is validated
- IllegalArgumentException, not AssertionError
basics
~20 sThe classpath lookup returned nothing. matchesJsonSchemaInClasspath resolves its argument with the thread context class loader's getResource, and a miss yields null, which the matcher factory rejects immediately. Nothing was validated, so this is a wiring bug, not a schema mismatch.
solid answer
~40 s`matchesJsonSchemaInClasspath(path)` is a one-liner: it calls `Thread.currentThread().getContextClassLoader().getResource(path)` and forwards the result to the `matchesJsonSchema(URL)` overload. A class loader returns `null` for a resource it cannot find, and the matcher factory's null guard turns that into `IllegalArgumentException("Schema to use cannot be null")`. So the message means **the schema was never found**, not that the peatland carbon-survey payload broke the schema — no validation happened at all, and the exception is not an `AssertionError`. The usual causes are a leading slash (class loader paths are already root-relative), an `src/test/resources` prefix left in the string, a file that never reached `target/test-classes` or `build/resources/test`, or a case mismatch that a developer's filesystem tolerates and CI does not. Confirm the fix by resolving the same path yourself before the request.
code
java · 15 linesimport static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchema;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
// classpath root, no leading slash:
// src/test/resources/schemas/carbon-survey-schema.json
Matcher<?> fromClasspath =
matchesJsonSchemaInClasspath("schemas/carbon-survey-schema.json");
// the String overload takes the schema DOCUMENT, never a path to one
Matcher<?> fromText =
matchesJsonSchema("{\"type\":\"object\",\"required\":[\"peatDepthCm\"]}");
// a filesystem location needs the File overload
Matcher<?> fromFile =
matchesJsonSchema(new File("schemas/carbon-survey-schema.json"));go deeper
Be ready to say the class loader returned null and the matcher rejected it. Remember the path is classpath-root relative with no leading slash and no src/test/resources prefix.
Explain why this is an IllegalArgumentException rather than an AssertionError, and that it is thrown while the body() argument is constructed — after the request has already been sent.
Show how you separate a path bug from a packaging bug: resolve the resource yourself, then look in the build output. Be able to explain why it passes locally and fails on a Linux agent.
Own the convention that stops it recurring — where schema documents live, how they are addressed, and a fixture that fails fast with the file name rather than deep inside a matcher.
## What the method actually does `matchesJsonSchemaInClasspath` is one of the smallest methods in REST Assured. It takes your string, asks the **thread context class loader** for a resource with that name, and hands whatever comes back to the `matchesJsonSchema(URL)` overload: ```java public static JsonSchemaValidator matchesJsonSchemaInClasspath(String pathToSchemaInClasspath) { return matchesJsonSchema(Thread.currentThread().getContextClassLoader().getResource(pathToSchemaInClasspath)); } ``` `ClassLoader.getResource` does not throw when a resource is missing — it returns `null`. The matcher factory then runs a null guard before it builds anything, and that guard throws `IllegalArgumentException` with the text *"Schema to use cannot be null"*. Every word of the message is about the **schema argument**, and none of it is about the response. ## The four things that make the lookup miss 1. **A leading slash.** `matchesJsonSchemaInClasspath("/schemas/carbon-survey-schema.json")` fails. `Class.getResource` treats a leading slash as "start at the root"; a `ClassLoader` lookup is root-relative already and a leading slash simply does not match. 2. **The source-tree prefix left in.** The build copies `src/test/resources` onto the classpath root, so the schema at `src/test/resources/schemas/carbon-survey-schema.json` is addressed as `schemas/carbon-survey-schema.json`. 3. **The resource never reached the build output.** Look in `target/test-classes` or `build/resources/test`. A resource filtered out by a `processTestResources` include/exclude rule, or a file added to the repository since the last build, is not there. 4. **Case.** A lookup inside a jar, and on a Linux CI agent, is case-sensitive. `Carbon-Survey-Schema.json` resolves on a case-insensitive laptop filesystem and disappears in the pipeline. A fifth, rarer cause is a runner that installs a context class loader which cannot see your test resources — shaded, OSGi-style or custom-classloader setups. If a plain `getClass().getClassLoader().getResource(path)` in the same test finds the file and the matcher does not, that is your answer. ## Why it is an `IllegalArgumentException`, not an `AssertionError` REST Assured signals a failed expectation with an `AssertionError`, which is what a runner reports as a test failure. An `IllegalArgumentException` from the matcher factory is thrown **while the argument to `body(...)` is being constructed** — the validator never ran, so nothing was compared. Reading the exception type first saves you from the classic wrong turn of editing the schema to make the "failure" go away. One detail surprises people: the HTTP request **did** go out. In `given().when().get("/surveys/PEAT-2291").then().body(matchesJsonSchemaInClasspath(...))`, Java evaluates the matcher as an argument to `body(...)`, which happens after `get(...)` has already sent the request. The stack trace points at your test line, not at the network. ## The overload that looks like a path but isn't The single most common "fix" that makes things worse is switching to `matchesJsonSchema(...)` and passing the same string: - `matchesJsonSchemaInClasspath(String)` — a **classpath resource path**. - `matchesJsonSchema(String)` — the **schema document itself**, parsed as JSON. - `matchesJsonSchema(File)` — a **filesystem path**, loaded eagerly. - `matchesJsonSchema(URL)` / `matchesJsonSchema(URI)` — a location, read lazily at match time. - `matchesJsonSchema(InputStream)` / `matchesJsonSchema(Reader)` — an open stream you supply. Feeding `"schemas/carbon-survey-schema.json"` to the `String` overload asks the JSON parser to read that filename as a JSON document. It cannot, and the module wraps the parse failure in its own `JsonSchemaValidationException` — a different exception with a different message, and still not a validation failure. The timing differs too, and it is a useful tell. `String`, `Reader` and `File` load the schema **eagerly**, while the matcher is being constructed, so a problem with the document itself throws before the assertion is even assembled. `URL` and `URI` keep the location and read it **lazily**, during matching. Since `matchesJsonSchemaInClasspath` funnels into the `URL` overload, a *missing* classpath schema fails eagerly with `IllegalArgumentException` but a *malformed* one fails later, inside the assertion, with `JsonSchemaValidationException`. Two different symptoms, one file. ## Fixing it, and proving the fix - **Print what the class loader sees.** `System.out.println(Thread.currentThread().getContextClassLoader().getResource("schemas/carbon-survey-schema.json"))` in the failing test tells you in one line whether the problem is the path or the build. - **List the build output.** If `build/resources/test/schemas/` is empty, the fix is in the build script, not in the test. - **Keep schema paths in one constant.** A `SCHEMAS = "schemas/"` prefix used by every carbon-survey test turns a class of typos into a single compile-time reference. - **Assert the resource exists in a fixture.** A one-line check in a setup method fails fast with a message that names the file, instead of failing later inside a matcher. - **Do not change overloads to escape the error.** If the file belongs on the classpath, keep the classpath overload and fix the path. The whole diagnosis is short because the mechanism is short: a class loader miss becomes `null`, and `null` becomes this exception before any response is looked at.
- The schema really is in src/test/resources but the lookup still misses. What do you check next?Whether it reached the build output. Maven copies `src/test/resources` to `target/test-classes` and Gradle to `build/resources/test`, and a resource excluded by a filtering or `processTestResources` rule never arrives. Confirm by resolving the same path with `getClass().getClassLoader().getResource(path)` in the failing test, and check the case of every segment — jar and Linux lookups are case-sensitive even when a developer's filesystem is not.
- What is thrown when the schema file is found but the JSON inside it is malformed?A `JsonSchemaValidationException`, the module's own `RuntimeException`, wrapping the parse failure as its cause. For the `String`, `Reader` and `File` overloads that happens while the matcher is being built, because those load the document eagerly. For `URL` and `URI` — which includes every `matchesJsonSchemaInClasspath` call — the document is read only during matching, so the same problem surfaces at assertion time instead.
saying these in an interview costs you the question
- Reads the message as the response failing schema validation
- Adds a leading slash, copying Class.getResource conventions
- Leaves the src/test/resources prefix inside the classpath path
- Switches to matchesJsonSchema(String) and passes the same path
- Assumes the HTTP request never went out
- Edits the schema document to make the error disappear