Troubleshooting
Each section starts with what holodeck prints. Search this page for the text you have.
A mocked request failed with a 400
A missing or mismatched fixture is a 400 from the mock server, and what the terminal shows depends on whether the test lets that error escape.
A test that lets it escape prints the request and, under it, the server's explanation.
💥 Fail Unexpected Test Failure: [400 Bad Request] POST (cors) - https://localhost:7358/api/user/ops/bulk.create
No meta was found for POST https://localhost:7358/api/user/ops/bulk.create. The expected cacheKey was /path/to/tests/json-api/.mock-cache/36ff0af3/POST::api_user_ops_bulk.create::0/f6c45fa65d57493c31a4f1b85eddcf6f. You may need to record a mock for this request.The second paragraph is the detail of the server's MOCK_NOT_FOUND response. MockServerHandler lifts it into the thrown error, with holodeck's own __xTestId query stripped out of the URL, so it reaches the terminal and the CI log. Searching a failed run for No meta was found finds it.
A test that catches the error and asserts against it prints only the assertions that failed. The explanation is still on the caught error, as error.content, but nothing prints it.
💥 Failed: 0ms Integration | @ember-data/json-api Cach.put(<ErrorDocument>):Useful errors are propagated by the CacheHandler
✅ Pass The error is an AggregateError
💥 Fail The error message is correct
💥 Fail The error status is correctDiagnostic prints the test id with every failure, whichever shape you get.
open test locally: https://localhost:7357?testId=546c9e3aThat id is the fixture directory. Look in .mock-cache/546c9e3a/ and you are in the right place. Open the printed URL to run the one test in a browser and read the failed request in the network panel, where the same MOCK_NOT_FOUND payload is the response body.
Compare the cacheKey in the message against what is on disk. The part that differs tells you which half of the mock is wrong.
- The directory is missing entirely. The fixture was never committed, or you are running in replay mode against a test you have not recorded yet.
- The URL segment differs. The mock URL and the request URL are not the same string. Query encoding is the usual cause. See Write the URL the way the server will see it.
- The trailing number differs. The test made more requests to that URL than it declared mocks for, or declared them in a different order.
- The file name is a hash rather than
res, and no file of that name exists. Thebodyoption and the real request body are not the same string. Open the recorded.meta.jsonand look atrequestBody. A correctly recorded body-matched mock stores an escaped JSON string. An object there means someone passed an object literal to the mock.
The test declared a mock it never requested
💥 Fail Unexpected Test Failure in afterEach: Holodeck: this test declared mocks it never requested.
GET users/never-requested (mocked 1, requested 0)
A mock that is never requested proves nothing. Remove it, or make the request it describes.When setTestId(this, null) runs from afterEach, holodeck compares the mocks the test declared against the requests it made, per method and URL, and fails the test over any mock left over. The report is added alongside the body's own assertions rather than in place of them, so a test whose body already failed shows both. It fires in record and replay alike.
Read the two counts. mocked 1, requested 0 is a mock the code under test never asked for. Delete it, or fix the test so the request happens. mocked 2, requested 1 is one declaration too many for a URL the test does hit. See Mock the same URL more than once.
An error earlier in the test body, including a failed recording, also leaves its mock unrequested, so this report often follows another failure. Fix the first one and this one goes with it.
Holodeck failed to record
💥 Fail Unexpected Test Failure: MockError: Holodeck failed to record GET users/1 (500 ). Cannot read properties of undefined (reading 'Content-Type')While recording, each mock helper posts its scaffold to the server, and throws when the server rejects it. The message carries the status and then the server's own explanation. The status text is blank because the server speaks HTTP/2, which has none.
In the example the scaffold was built by hand with mock and left out headers, which the six helpers always supply. A fixture the server cannot write, because .mock-cache is not writable or the disk is full, reports the filesystem error in the same place. Replay posts nothing, so this only appears in a recording run.
The test id is missing
MockServerHandler is not configured with a testId. Use setTestId to set the testId for each testHolodeck looks the test up by object identity, and it was handed an object it does not know.
Check three things in this order. The global hooks that call setTestId are installed. The object passed to new MockServerHandler(...) is the same test context setTestId received. The test body is a function rather than an arrow function, so this is the test context at all.
Cannot call "mock" before configuring a testId. Use setTestId to set the testId for each testSame cause, reported from a mock helper instead of from the request handler.
MockServerHandler is already configured with a testId.setTestId ran twice for one test context. A second setupGlobalHooks block or a stray per-test call is the usual reason.
The request never reached the handler
MISSING_X_TEST_ID_HEADERThe server received a request carrying no __xTestId query parameter, which means the request did not go through MockServerHandler. The code keeps its historical name, but the message names the query parameter and says what to do. Put the handler in the chain ahead of Fetch, and check that the code under test goes through that RequestManager rather than calling fetch directly.
The certificate is missing
Error: SSL certificate or key not found, you may need to run `pnpm dlx @warp-drive/holodeck ensure-cert`
at getCertInfo (.../@warp-drive/diagnostic/dist-server/index.js:1254:67)
at launch (.../@warp-drive/diagnostic/dist-server/index.js:1336:27)Both @warp-drive/diagnostic and holodeck serve over TLS, and both read the same certificate. Diagnostic starts first, so this is usually the first thing a machine without a certificate hits, and holodeck's banner never prints. Run the certificate step from Server setup.
Holodeck looks for HOLODECK_SSL_CERT_PATH and HOLODECK_SSL_KEY_PATH, and falls back to $HOME/holodeck-localhost.pem and $HOME/holodeck-localhost-key.pem when they are unset. The fallback is announced, not an error.
HOLODECK_SSL_CERT_PATH was not found in the current environment. Setting it to default value of /Users/you/holodeck-localhost.pemA run that prints those two lines and then serves normally is fine. Set the variables only when the certificate lives somewhere other than your home directory.
Could not determine a startup file for shell: /usr/bin/nu.
Holodeck falls back to /Users/you/holodeck-localhost.pem when the environment variables
are unset, so the certificate above already works as it is.This is information, not an error. ensure-cert issues the certificate before it looks at your shell, and it exits normally on a shell it cannot write a profile for. It goes on to print the two export lines in case you want them, but the certificate is already where holodeck looks.
mkcert was not found on your PATH. Holodeck serves over TLS and uses mkcert to
issue a certificate for localhost.ensure-cert checks for mkcert before it does anything else and exits with the install commands for each platform. Install it and run the command again.
The suite starts and then times out
⚠️ Diagnostic Watchdog: No browser reported in within 90s of launch. Assuming a hung browser and exiting.
connections: 2 tcp, 1 tls handshakes
requests served: 2
websocket connections: 0
server events: 870ms tlsClientError: socket hang up
browsers: chrome#42 pid=90709 runningNothing before this names a cause. Holodeck's banner prints, the browser launches, and 90 seconds later the watchdog gives up and prints what the server saw in the meantime. A browser that fetched a page or two and never opened a websocket, as above, has usually been served a test bundle that does not exist. Check that dist-test/index.html is there, and run the suite through the command that builds first, which at this repository's root is pnpm test. The wait is browserStartTimeout in the launch config, 90 seconds by default.
The holodeck server is not running
Promise rejected during "…": Failed to fetchEvery mock fails this way when nothing is listening on the host passed to setConfig. Safari words it as Load failed. Each test also fails from afterEach with a report that its mocks were never requested, which follows from the first error.
Check that whatever serves the suite also launched holodeck, and that its banner printed the port setConfig names. A suite opened from an app's dev server rather than from its test runner usually has no holodeck behind it. See Holodeck in dev mode for one way to start it there.
The port was taken
Holodeck binds the port it is given and never picks another, because the browser works out the mock server's URL by adding one to the test server's port. A conflict retries the same port a few times and then fails.
Stop whatever holds the port. A test run that was killed rather than shut down is the usual culprit.
Launched from the wrong directory
Package name not found in package.jsonlaunchProgram reads package.json from the current working directory, and writes .mock-cache there too. Start the test runner from the test app's own directory.