Troubleshooting

Common issues and their solutions. If you don't find your answer here, open an issue on GitHub.

Stale Injection Detected

Symptom: FlakeMonster warns "Stale injection detected" when running test or inject.

Cause: A previous run was interrupted before restore completed. The manifest file still exists, indicating that source files contain injected code from a prior session.

Solution: Run flake-monster restore to clean up the stale injection. The test command also auto-restores before starting, so in most cases this resolves itself on the next run.

$ npx flake-monster restore
Restored 12 file(s), removed 47 injection(s)

Tip: If you interrupted a run with Ctrl+C, FlakeMonster's shutdown handler usually restores files automatically. This warning only appears if the process was killed forcefully (e.g., kill -9) or crashed unexpectedly.

Automatic Crash Recovery (In-Place Mode)

When using in-place mode, the test command wraps its entire run loop in error handling. If a crash or unexpected error occurs mid-run, FlakeMonster automatically attempts to restore all source files before exiting. This prevents your working tree from being left in an injected state after a failure.

If automatic recovery succeeds, you will see:

Error during in-place test run. Attempting to restore source files...
Source files restored.

If automatic recovery itself fails (e.g., due to disk errors), FlakeMonster will print the error and suggest running flake-monster restore manually.

Double Injection

Symptom: Delays appear twice in the same file, or you see nested marker comments.

Cause: Injecting into files that already have injections. This can happen if the manifest was manually deleted while injected code was still present.

Solution: The manifest guard prevents this automatically under normal usage. If you see doubled delays, run flake-monster restore first, then re-inject:

$ npx flake-monster restore
$ npx flake-monster inject --seed 12345

Tip: If restore without --recover doesn't fully clean up (because the manifest is missing), use recovery mode: flake-monster restore --recover. This scans for all traces of injected code regardless of manifest state.

Cannot Find Module flake-monster.runtime.js

Symptom: Tests fail with ERR_MODULE_NOT_FOUND or Cannot find module './flake-monster.runtime.js'.

Cause: The runtime file wasn't copied to the project root, or the relative import path is wrong. This typically happens when injection is interrupted before the runtime copy step completes.

Solution: Run flake-monster restore and re-inject. The runtime is copied automatically during injection.

$ npx flake-monster restore
$ npx flake-monster inject

If the issue persists, check that your glob patterns match files within the project root. Files outside the project root will produce incorrect runtime import paths.

Import Path Is Wrong

Symptom: Injected files import the runtime with an incorrect relative path (e.g., ../../../flake-monster.runtime.js when it should be ../flake-monster.runtime.js).

Cause: FlakeMonster computes import paths relative from each file to the project root. Unusual directory structures (symlinks, files outside the project tree, deeply nested paths) may produce incorrect paths.

Solution: Ensure your source files are within the project root. The path is computed based on directory depth:

File Location Generated Import Path
app.js ./flake-monster.runtime.js
src/user.js ../flake-monster.runtime.js
src/api/client.js ../../flake-monster.runtime.js

If you have files in unexpected locations, narrow your include patterns to only match files within the project tree.

node:test Crashes with Exit Code 7

Symptom: When using node --test, the process exits with code 7 and no useful output.

Cause: Node 20 does not have a built-in json test reporter. Using --test-reporter json causes this crash.

Solution: FlakeMonster automatically uses --test-reporter tap instead when it detects node --test in your command. If you're specifying the reporter manually, use tap not json:

$ npx flake-monster test --cmd "node --test --test-reporter tap test/*.test.js"

Tip: The default spec reporter in node --test outputs to stderr, not stdout. FlakeMonster's auto-detection handles this, but if you're using a custom command, make sure the reporter writes to stdout so the parser can read it.

No Tests Parsed

Symptom: FlakeMonster completes runs but shows "0 tests found" or can't identify individual test results.

Cause: The test runner's output format wasn't recognized by the parser. This happens when FlakeMonster can't auto-detect your runner, or when the runner produces output in an unexpected format.

Solution:

Runner Required Flag Example
Jest --json npx jest --json
Playwright --reporter=json npx playwright test --reporter=json
node:test --test-reporter tap node --test --test-reporter tap
TAP (native TAP output) node --test --test-reporter tap

FlakeMonster auto-adds these flags for known runners, but custom setups or wrapper scripts may need manual configuration.

$ npx flake-monster test --runner jest --cmd "npx jest --json"

Linter/Formatter Rewrites Injected Code

Symptom: ESLint, Prettier, or other tools modify the injected code (reformatting, adding semicolons, changing quotes, etc.).

Cause: This is expected behavior. Linters and formatters will reformat any code they find, including injected FlakeMonster statements.

Solution: This is fine and does not affect FlakeMonster's operation. The removal process uses text-based matching on two unique identifiers that survive any reformatting:

Restore will work correctly even after linters, formatters, or other tools have modified the injected code. No special configuration is needed.

Tip: If you want to suppress linter warnings during injection, you can add flake-monster.runtime.js to your .eslintignore or equivalent. But this is purely cosmetic — it won't affect FlakeMonster's behavior.

All Tests Always Fail

Symptom: Every test fails in every run, not just some.

Cause: These are pre-existing bugs, not flakiness. FlakeMonster classifies tests that fail in all runs as "always-failing" and reports them separately from flaky tests.

Solution: Fix the underlying test failures first, then re-run FlakeMonster to check for timing-related flakiness. A test must pass in at least one run and fail in at least one other to be classified as flaky.

# First, make sure tests pass without FlakeMonster
$ npm test
# If tests pass, then run FlakeMonster
$ npx flake-monster test --cmd "npm test"

Tip: Check the JSON output (--format json) for the analysis.alwaysFailingTests array to see which tests are consistently broken.

Tests Pass Locally But Fail in CI

Symptom: FlakeMonster finds no flakes locally but the GitHub Action reports failures.

Cause: CI environments have different timing characteristics compared to local machines: slower disk I/O, shared CPU, network latency, and less available memory. These differences can expose timing-dependent issues that fast local machines hide.

Solution: Try running locally with higher delays to simulate a slower environment:

$ npx flake-monster test --min-delay 10 --max-delay 200 --cmd "npm test"

Or use --mode hardcore for more thorough coverage with maximum injection density:

$ npx flake-monster test --mode hardcore --min-delay 10 --max-delay 200 --runs 20 --cmd "npm test"

Tip: CI environments often expose real flakiness that your local machine is fast enough to mask. If FlakeMonster finds flakes in CI but not locally, those are genuine issues worth investigating.

Recovery Mode

Symptom: Normal restore doesn't fully clean up injected code (rare edge case). This can happen if the manifest file was corrupted, manually deleted, or if files were moved after injection.

Solution: Use recovery mode for interactive cleanup:

$ npx flake-monster restore --recover

Recovery mode scans all source files for any remaining traces of FlakeMonster injection. It matches on three patterns and labels each match with a reason:

Example output:

$ npx flake-monster restore --recover
Scanning all source files...

  src/api/client.js (3 matches):
    L2  [import] import { __FlakeMonster__ } from '../flake-monster.runtime.js';
    L15 [stamp]  await __FlakeMonster__(23); // @flake-monster[jt92-se2j!] v1
    L42 [stamp]  await __FlakeMonster__(7);  // @flake-monster[jt92-se2j!] v1

  src/utils/cache.js (2 matches):
    L1  [import] import { __FlakeMonster__ } from '../flake-monster.runtime.js';
    L28 [stamp]  await __FlakeMonster__(31); // @flake-monster[jt92-se2j!] v1

  Total: 5 line(s) across 2 file(s)

  Remove these lines? (y/N) y
  Recovered 2 file(s), removed 5 line(s)

Each matched line is shown with its line number and match reason before any changes are made, so you can review exactly what will be removed.

Getting Help

If you're stuck on an issue not covered here, there are a few ways to get help:

When reporting an issue, including the seed number from the failing run is especially helpful. It allows the maintainers to reproduce the exact same injection pattern:

$ npx flake-monster test --runs 1 --seed 948271536 --cmd "npm test"