Skip to content

Cache restore always fails on Windows when absolute ("drive-letter") paths are used #154

@bartekpacia

Description

@bartekpacia

this issue is created with Cursor

TLDR: seems like the function is_absolute_path is POSIX-only


Summary

On Windows agents, caching an absolute path such as C:/cache/foo (or C:\cache\foo) saves an archive but never restores it. Every restore fails with:

tar: C\:/cache/foo: Not found in archive

When soft-fail is enabled the build then silently continues with a cold cache, so the failure is easy to miss. This affects the zstd, tgz, and zip compression backends.

Environment

  • Plugin: cache-buildkite-plugin
  • Compression backends affected: zstd, tgz, zip
  • Agent OS: Windows, using GNU tar under Git Bash / msys2
  • Backends: reproduces on the generic compress/restore path (observed in production with the s3 backend)

Root cause

All three wrappers in compression/ decide whether to pass tar's -P (--absolute-names) flag using:

is_absolute_path() { [ "${1:0:1}" = "/" ]; }

This only treats POSIX /-rooted paths as absolute. A Windows path begins with a drive letter (C:/… or C:\…), so is_absolute_path returns false for it. The two sides then disagree:

  • Compress: runs tar without -P. GNU/msys tar strips the drive prefix and stores the member as a relative path, e.g. cache/foo, emitting tar: Removing leading 'C:/' from member names.
  • Restore: still without -P, asks tar to extract the member named C:/cache/foo. That member does not exist in the archive (it was stored as cache/foo) → Not found in archive → restore fails.

Because compress and restore compute the member name differently, the cache can never be restored for an absolute Windows path.

A secondary consequence: if save skips when the key already exists, the poisoned/empty-from-the-consumer's-perspective archive persists until the cache key rotates, so the cache stays permanently cold.

Reproduction (Git Bash on Windows)

# from the plugin root
mkdir -p /c/cache/foo && echo hi > /c/cache/foo/a.txt

# compress an ABSOLUTE windows path — note the "removing leading drive letter" warning
compression/zstd_wrapper compress "C:/cache/foo" out.tzst

rm -rf /c/cache/foo

# restore fails: the member was stored as cache/foo, not C:/cache/foo
compression/zstd_wrapper decompress out.tzst "C:/cache/foo"
# => tar: C:/cache/foo: Not found in archive

A relative path (e.g. compress "cache/foo" / decompress out.tzst "cache/foo") works, because both sides treat it consistently as relative — which is why the bug only surfaces for absolute cache paths.

Impact

  • Any Windows pipeline that caches an absolute path gets a permanent, silent cache miss.
  • With soft-fail enabled, builds appear green while re-doing all the work the cache was meant to skip.
  • Relative (in-checkout) cache paths are unaffected, which is why this can go unnoticed for a long time.

Suggested fix

Teach is_absolute_path about Windows drive-absolute paths, in compression/zstd_wrapper, compression/tgz_wrapper, and compression/zip_wrapper:

is_absolute_path() {
  case "$1" in
    /*) return 0 ;;               # POSIX absolute
    [A-Za-z]:[\\/]*) return 0 ;;  # Windows drive-absolute: C:/... or C:\...
    *) return 1 ;;
  esac
}

With this change both compress and restore pass -P, so tar stores and looks up the same absolute member name and the cache restores correctly.

Notes

  • The fix needs to land in all three wrappers, since they each carry their own copy of is_absolute_path.
  • Existing cache objects created before the fix were stored with the drive prefix stripped; consumers using an absolute path will need their cache key rotated (or the stale objects deleted) to pick up a correctly-stored archive, since save skips when the key already exists.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions