Skip to content

Concurrent get_kernel calls on a fresh machine fail with TUFError: Failed to refresh TUF metadata #970

Open

Description

@albertvillanova

Description

Since kernels 0.18.0, get_kernel verifies the sigstore signature of Hub kernels by default (SignatureValidator, added in #828). When no verification receipt is cached yet, verify_signature calls sigstore.verify.Verifier.production(), which refreshes the TUF trust root in ~/.local/share/sigstore-python/tuf/. python-tuf replaces the root.json symlink there with a non-atomic os.remove + os.symlink, so when several processes load a kernel at the same time some of them fail with FileExistsError, raised as sigstore.errors.TUFError: Failed to refresh TUF metadata.

This is the normal situation of a multi-GPU launch (torchrun, accelerate launch) on a fresh machine, container or CI runner: every rank loads the same kernel at the same time. It is not a rare race: with 8 processes, every trial I ran had at least one failed load.

The error is not caught by SignatureValidator, although its docstring says verification issues are currently reported as warnings, so the kernel load itself fails.

python-tuf documents that concurrent instances sharing a cache are not supported, see:

We hit this in the TRL CI, where pytest-xdist workers load kernels-community/flash-attn3 at the same time: huggingface/trl#8130

Steps to reproduce

# Load the same Hub kernel from 8 processes at the same time on a fresh machine:
# empty sigstore TUF cache (in HOME) and empty kernels cache (no signature verification receipts).
import multiprocessing as mp
import os
import tempfile


def load(barrier):
    from kernels import get_kernel

    barrier.wait()
    get_kernel("kernels-community/activation", version=1)


if __name__ == "__main__":
    home = tempfile.mkdtemp()
    os.environ["HOME"] = home
    os.environ["KERNELS_CACHE"] = os.path.join(home, "kernels")
    ctx = mp.get_context("spawn")
    barrier = ctx.Barrier(8)
    procs = [ctx.Process(target=load, args=(barrier,)) for _ in range(8)]
    for p in procs:
        p.start()
    for p in procs:
        p.join()
    print(f"{sum(p.exitcode != 0 for p in procs)}/8 loads failed")

Output (3 out of 3 runs): 2/8 loads failed, each with:

Traceback (most recent call last):
  File ".../sigstore/_internal/tuf.py", line 117, in __init__
    self._updater = Updater(
  File ".../tuf/ngclient/updater.py", line 148, in __init__
    self._update_root_symlink()
  File ".../tuf/ngclient/updater.py", line 373, in _update_root_symlink
    os.symlink(current, linkname)
FileExistsError: [Errno 17] File exists: 'root_history/15.root.json' -> '.../tmpdsposr6f/.local/share/sigstore-python/tuf/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/root.json'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "repro_kernels.py", line 12, in load
    get_kernel("kernels-community/activation", version=1)
  File ".../kernels/load.py", line 175, in get_kernel
    return get_kernel_with_resolver(
  File ".../kernels/load.py", line 99, in get_kernel_with_resolver
    tree_only_local.validate_kernel(kernel_validator)
  File ".../kernels/deps.py", line 72, in validate_kernel
    validator.validate_kernel(kernel=self.location)
  File ".../kernels/validate.py", line 249, in validate_kernel
    validator.validate_kernel(kernel=kernel)
  File ".../kernels/validate.py", line 186, in validate_kernel
    result = verify_signature(kernel.variant_path, policy=self.policy, location=location)
  File ".../kernels/verify.py", line 243, in verify_signature
    verifier = Verifier.production()
  File ".../sigstore/verify/verifier.py", line 112, in production
    config = ClientTrustConfig.production(offline=offline)
  File ".../sigstore/models.py", line 937, in production
    return cls.from_tuf(DEFAULT_TUF_URL, offline)
  File ".../sigstore/models.py", line 963, in from_tuf
    updater = TrustUpdater(url, offline, bootstrap_root)
  File ".../sigstore/_internal/tuf.py", line 129, in __init__
    raise TUFError("Failed to refresh TUF metadata") from e
sigstore.errors.TUFError: Failed to refresh TUF metadata

Loading the kernel once in a single process first (so the verification receipt is cached) avoids it: the 8 concurrent loads then succeeded in 5 out of 5 runs.

Expected behavior

Concurrent get_kernel calls from several processes succeed, as with kernels 0.17. For example:

  • serialize the signature verification across processes, e.g. with a file lock around Verifier.production() / the verification, or
  • report a failure to refresh the TUF metadata as the warning that the SignatureValidator docstring describes, instead of failing the load.

Environment

  • kernels version: 0.18.1 (sigstore 4.5.1, tuf 7.0.1)
  • Python version: 3.13.9
  • PyTorch version: 2.15.0+cu132
  • CUDA version: 13.2
  • GPU model: NVIDIA H100 80GB HBM3
  • OS: Linux-6.8.0-1057-aws-x86_64-with-glibc2.35

Additional context

The race also happens with kernels-community/flash-attn3 (3 out of 3 trials with 8 processes).

Activity

  1. albertvillanova commented on Nov 5, 2026

    @albertvillanova
    MemberAuthor

    The race is in python-tuf's _update_root_symlink. Creating the link under a per-process temp name and moving it into place with os.replace fixes it: 0 failed loads in 15 trials with 8 and 16 processes. I suggested this on the open python-tuf PR on these lines:

    sigstore accepts tuf<8, so a python-tuf 7.x release with this fix would reach kernels users without any change here, but that PR is still awaiting review. In the meantime, catching TUFError around Verifier.production() in verify_signature and returning a SignatureVerificationFailure would turn the failure into the warning that SignatureValidator documents, instead of failing the load.

  2. albertvillanova commented on Nov 9, 2026

    @albertvillanova
    MemberAuthor

    #966 also fixes this for users who don't opt into kernels[sigstore]: with sigstore not installed, 8 concurrent loads failed in 0/10 trials, against 5/5 with kernels 0.18.1, which always installs sigstore.

    The next transformers release requires kernels>=0.18.0,<0.19, which would expose multi-GPU training (one kernel load per rank) on fresh machines. Could you release a patch with #966 before then? transformers can then raise its floor to that version.

  3. albertvillanova commented on Nov 9, 2026

    @albertvillanova
    MemberAuthor

    @danieldk (author of #828 and #966) transformers main already requires kernels>=0.18.0,<0.19, so its next release will pull kernels 0.18.1 and sigstore with it, and multi-GPU runs that load a Hub kernel on fresh machines will crash: 8-rank torchrun loads of kernels-community/flash-attn3 crashed in 5/5 runs. A patch release with #966 before then would avoid that. Tracked on the transformers side in:

  4. sayakpaul commented on Nov 11, 2026

    @sayakpaul
    Member

    I think we released a patch?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions