dotnet/runtime · error · RuntimeError

Error, unclean replay.

Error message

Error, unclean replay.

What it means

Raised in `__verify_final_mch__` (superpmi.py:1478-1479), only reached when `--clean` is set. After cleaning, it replays the final MCH against the same JIT used for collection; if SuperPMIReplay.replay() returns False (any context failed), the collection is considered non-deterministic/unclean and rejected. A clean collection must replay every context error-free.

Source

Thrown at src/coreclr/scripts/superpmi.py:1479

        if not os.path.isfile(self.toc_file):
            raise RuntimeError("Error, toc file not created correctly at: %s" % self.toc_file)

    def __verify_final_mch__(self):
        """ Verify the resulting MCH file is error-free when running SuperPMI against it with the same JIT used for collection.

        Notes:
            <SuperPmiPath> -p -f <s_finalFailMclFile> <s_finalMchFile> <jitPath>
        """

        logging.info("Verifying MCH file")

        mch_files = [ self.final_mch_file ]
        spmi_replay = SuperPMIReplay(self.coreclr_args, mch_files, self.jit_path)
        passed = spmi_replay.replay()

        if not passed:
            raise RuntimeError("Error, unclean replay.")

    def __process_for_ci__(self):
        """ Helix doesn't upload zero-sized files. Sometimes we end up with zero-sized .mch files if
            there is no data collected. Convert these to special "sentinel" files that are later processed
            by "merge-mch" by deleting them. This prevents the <HelixWorkItem.DownloadFilesFromResults>
            file download to succeed (because the file exists) and the MCH merge to also succeed.
        """

        logging.info("Process MCH files for CI")

        if os.path.getsize(self.final_mch_file) == 0:
            # Convert to sentinel file
            logging.info("Converting zero-length MCH file {} to ZEROLENGTH sentinel file".format(self.final_mch_file))
            with open(self.final_mch_file, "w") as write_fh:
                write_fh.write("ZEROLENGTH")

################################################################################
# SuperPMI Replay helpers

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Re-run the collection with `--clean` (it strips failing contexts) until replay passes; if it never passes, investigate the failing context reported by the replay.
  2. Ensure tiering is explicitly controlled (--tiered_compilation / --tiered_pgo) so collection is deterministic.
  3. Confirm the JIT used for the verification replay is the same clrjit used during collection (same Core_Root/build).
Defensive patterns

Strategy: retry

Validate before calling

# Best-effort: ensure deterministic collection env before clean+verify
# (determinism is enforced via DOTNET_ vars; nothing to validate offline)
pass

Try / catch

attempt = 0
passed = False
while not passed and attempt < 3:
    attempt += 1
    try:
        collection.__verify_final_mch__()
        passed = True
    except RuntimeError as e:
        if 'unclean replay' not in str(e):
            raise
        logging.warning('unclean replay on attempt %d; re-running clean', attempt)
        collection.__create_clean_mch_file__()  # strip more failing contexts
if not passed:
    raise SystemExit('MCH never replayed cleanly after retries')

Prevention

When it happens

Trigger: Collecting with `--clean` where the verification replay of the final MCH with the collection JIT reports at least one failing context. This indicates the JIT did not deterministically reproduce some captured method contexts.

Common situations: Non-deterministic JIT behavior (e.g. randomness/tiering leaking in), time- or thread-dependent compilations, an MCH that still contains contexts that fail to replay after stripping, or using a JIT build that differs subtly from the collection-time JIT.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/7229d7e8f2d2beed. Report an issue: GitHub.