Skip to content

tart prune leaves broken OCI tag symlinks after deleting their digest #1293

Description

@TyceHerrman

Description

tart prune can delete an OCI digest directory while leaving its tag symlink behind as a broken link.

This does not retain the VM's disk usage, but it leaves the OCI cache in an inconsistent state until the hidden garbage collector is run separately.

Steps to reproduce

  1. Pull a tagged image:

    tart pull ghcr.io/cirruslabs/macos-tahoe-base:latest
  2. Prune the cache:

    tart prune --entries caches --space-budget 0
  3. Inspect the cache:

    $ du -sh ~/.tart/cache
      0B    ~/.tart/cache
    
    $ du -shA ~/.tart/cache
    3.5K    ~/.tart/cache
    
    $ find ~/.tart/cache -type l -print -exec readlink {} \;
    ~/.tart/cache/OCIs/ghcr.io/cirruslabs/macos-tahoe-base/latest
    ~/.tart/cache/OCIs/ghcr.io/cirruslabs/macos-tahoe-base/sha256:a8e1c8305758643f513fdccdd829c2243687c60791083dea42f73f0b7aeb435c

The digest target no longer exists, but the latest symlink remains.

Expected behavior

After pruning an OCI digest, Tart should remove any tag symlinks that now point to the deleted digest.

Actual behavior

The digest directory is deleted successfully, but its tag symlink remains broken.

The current workaround is a second command:

tart prune --gc

Root cause

VMStorageOCI.prunables() explicitly excludes symlinks and returns the digest VMDirectory objects:

https://github.com/openai/tart/blob/2.34.0/Sources/tart/VMStorageOCI.swift

The normal prune paths call Prunable.delete() directly:

https://github.com/openai/tart/blob/2.34.0/Sources/tart/Commands/Prune.swift

This bypasses VMStorageOCI.delete(_:), which is the deletion path that calls gc().

Additionally, when --gc is combined with a pruning criterion, Prune.run() invokes GC before deleting entries. Therefore this still leaves links broken by the subsequent prune:

tart prune --gc --entries caches --space-budget 0

Suggested fix

Run VMStorageOCI.gc() once after OCI cache pruning completes.

A regression test could:

  1. Create a digest directory and tag symlink.
  2. Prune the digest using both the age and space-budget paths.
  3. Verify that the digest and newly broken tag symlink are both removed.
  4. Verify that combining --gc with a pruning criterion also leaves no broken links.

Related history

Environment

Tart 2.34.0
macOS 27.0
Architecture: arm64

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