{"record":{"id":"44b410dd7234a7b9","repo":"dagger/dagger","slug":"acquire-cache-volume-snapshot-q-opened-snapshot","errorCode":null,"errorMessage":"acquire cache volume snapshot %q: opened snapshot %q","messagePattern":"acquire cache volume snapshot %q: opened snapshot %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/cache.go","lineNumber":234,"sourceCode":"\n\tif s.entries == nil {\n\t\ts.entries = make(map[string]*cacheVolumeStoreEntry)\n\t}\n\tif entry, ok := s.entries[snapshotID]; ok {\n\t\tentry.refs++\n\t\treturn entry.ref, (&cacheVolumeStoreReleaser{store: s, snapshotID: snapshotID}).release, nil\n\t}\n\n\tref, err := open(ctx)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif ref == nil {\n\t\treturn nil, nil, fmt.Errorf(\"acquire cache volume snapshot %q: nil mutable ref\", snapshotID)\n\t}\n\tif ref.SnapshotID() != snapshotID {\n\t\t_ = ref.Release(context.WithoutCancel(ctx))\n\t\treturn nil, nil, fmt.Errorf(\"acquire cache volume snapshot %q: opened snapshot %q\", snapshotID, ref.SnapshotID())\n\t}\n\n\ts.entries[snapshotID] = &cacheVolumeStoreEntry{\n\t\tref:  ref,\n\t\trefs: 1,\n\t}\n\treturn ref, (&cacheVolumeStoreReleaser{store: s, snapshotID: snapshotID}).release, nil\n}\n\nfunc (s *cacheVolumeStore) register(ctx context.Context, ref bkcache.MutableRef) (func(context.Context) error, error) {\n\tif ref == nil {\n\t\treturn nil, fmt.Errorf(\"register cache volume snapshot: nil mutable ref\")\n\t}\n\tsnapshotID := ref.SnapshotID()\n\tif snapshotID == \"\" {\n\t\t_ = ref.Release(context.WithoutCancel(ctx))\n\t\treturn nil, fmt.Errorf(\"register cache volume snapshot: empty snapshot ID\")\n\t}","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/core/cache.go#L216-L252","documentation":"cacheVolumeStore.acquire (core/cache.go) opens a cache volume snapshot by ID and validates that the MutableRef returned by the open callback reports the same SnapshotID. This error means the opened ref's snapshot ID differs from the requested one, so the ref is released and acquire fails to prevent sharing the wrong snapshot. It guards an internal invariant between the requested ID and the snapshotter's answer.","triggerScenarios":"Calling acquire (via the cache volume attach path) where open(ctx) returns a ref for a different snapshot than the requested snapshotID — e.g. a snapshotter returning a recycled/recreated snapshot, or a mocked open function returning the wrong ref.","commonSituations":"Running against a modified or buggy containerd snapshotter; engine races where the snapshot is deleted and re-created between ID derivation and open; test doubles that do not honor the requested snapshot ID.","solutions":["Fix the open callback so it opens the snapshot for exactly the requested snapshotID.","Restart/upgrade the Dagger engine to rule out snapshotter state corruption.","If using mocks in tests, make SnapshotID() return the requested ID.","Check for concurrent delete/recreate of the snapshot and serialize the acquire path."],"exampleFix":"// before (mock opens arbitrary snapshot)\nopen := func(ctx context.Context) (bkcache.MutableRef, error) { return arbitraryRef, nil }\n// after\nopen := func(ctx context.Context) (bkcache.MutableRef, error) { return snap.GetRef(ctx, snapshotID) }","handlingStrategy":"validation","validationCode":"if ref == nil || ref.SnapshotID() != snapshotID {\n    return fmt.Errorf(\"open callback returned snapshot %q, want %q\", refSafeID(ref), snapshotID)\n}","typeGuard":"func refMatchesSnapshot(ref bkcache.MutableRef, id string) bool {\n    return ref != nil && ref.SnapshotID() == id\n}","tryCatchPattern":"ref, rel, err := store.acquire(ctx, snapshotID, open)\nif err != nil {\n    var mismatchErr *fmt.WrapError // log and fall back to re-opening\n    return fmt.Errorf(\"acquire failed: %w\", err)\n}","preventionTips":["Always open snapshots by the exact requested ID in open callbacks","Keep mock snapshotters faithful: SnapshotID() must echo the requested ID","Pin and test against the engine's expected containerd/snapshotter version","Watch for concurrent snapshot deletion/recreation around acquire"],"tags":["cache-volume","snapshot","internal-invariant","dagger-engine"],"backgroundTag":"snapshot-id-mismatch","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}