stride3d/stride · error · InvalidOperationException

Truncated cache indirection file

Error message

Truncated cache indirection file

What it means

While loading bytecode through the shared application cache, the indirection file should contain exactly one ObjectId (the output hash). If the stream yields fewer bytes than ObjectId.HashSize, the cache considers the indirection file corrupt/truncated and throws InvalidOperationException instead of loading garbage.

Solutions

  1. Delete the truncated indirection file (and the stale app cache directory) so it is regenerated on next compile.
  2. Clear VirtualFileSystem.ApplicationCache contents and rebuild/recompile effects.
  3. Ensure cache writes complete atomically (write temp + rename) in environments prone to interruption.

Example fix

// before
var hash = LoadIndirection(path); // throws: Truncated cache indirection file
// after
if (!File.Exists(indirectionPath) || new FileInfo(indirectionPath).Length < ObjectId.HashSize)
    File.Delete(indirectionPath); // force regeneration, then retry compile
Defensive patterns

Strategy: try-catch

Validate before calling

using (var s = VirtualFileSystem.ApplicationCache.OpenStream(indirectionPath, VirtualFileMode.Open, VirtualFileAccess.Read)) if (s.Length < ObjectId.HashSize) { /* treat cache entry as corrupt: delete and recompile */ }

Type guard

null

Try / catch

try { bytecode = LoadFromCache(hash); } catch (InvalidOperationException ex) when (ex.Message == "Truncated cache indirection file") { InvalidateCacheEntry(); result = RecompileEffect(); }

Prevention

When it happens

Trigger: The ApplicationCache indirection file exists but is shorter than ObjectId.HashSize — typically from an interrupted write, disk-full condition, crash mid-write, or a partially synced cache directory.

Common situations: Killed builds/processes leaving partial cache files; shared/roaming cache folders synced incompletely; manual copying of cache directories.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/1410ab4a369f0958. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Effects/Compiler/EffectCompilerCache.cs:150

                // 2.5) Try to load from application cache (DynamicCache entries)
                //      Uses indirection: {effectInputHash}.cache contains the output hash,
                //      then {outputHash} contains the actual bytecode (shared across inputs with same output).
                // ------------------------------------------------------------------------------------------------------------
                if (bytecode.Key == null)
                {
                    var indirectionPath = GetAppCacheIndirectionPath(mixin.Name, effectInputHash);
                    if (VirtualFileSystem.ApplicationCache.FileExists(indirectionPath))
                    {
                        try
                        {
                            // Read the output hash from the indirection file
                            ObjectId outputHash;
                            using (var indirStream = VirtualFileSystem.ApplicationCache.OpenStream(
                                indirectionPath, VirtualFileMode.Open, VirtualFileAccess.Read, VirtualFileShare.Read))
                            {
                                var outputHashBuffer = new byte[ObjectId.HashSize];
                                if (indirStream.Read(outputHashBuffer, 0, ObjectId.HashSize) != ObjectId.HashSize)
                                    throw new InvalidOperationException("Truncated cache indirection file");
                                outputHash = new ObjectId(outputHashBuffer);
                            }

                            // Load bytecode from the shared output file
                            var bytecodePath = GetAppCachePath(mixin.Name, outputHash);
                            if (VirtualFileSystem.ApplicationCache.FileExists(bytecodePath))
                            {
                                using var stream = VirtualFileSystem.ApplicationCache.OpenStream(
                                    bytecodePath, VirtualFileMode.Open, VirtualFileAccess.Read, VirtualFileShare.Read);
                                var loadedBytecode = EffectBytecode.FromStream(stream);
                                if (loadedBytecode != null && !IsBytecodeObsolete(loadedBytecode))
                                {
                                    bytecode = new KeyValuePair<EffectBytecode, EffectBytecodeCacheLoadSource>(
                                        loadedBytecode, EffectBytecodeCacheLoadSource.DynamicCache);
                                    bytecodes[outputHash] = bytecode;
                                }
                            }
                        }

View on GitHub (pinned to 96fad776d2)