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
- Delete the truncated indirection file (and the stale app cache directory) so it is regenerated on next compile.
- Clear VirtualFileSystem.ApplicationCache contents and rebuild/recompile effects.
- 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
- Write cache files atomically (temp file + rename)
- Clear the application cache after interrupted builds
- Avoid sharing a live cache directory over sync tools
- Monitor cache directory for zero/partial-size files
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
- Bundle has not been properly written
- Can't read beyond end of stream.
- Could not open file.
- Data chunk has invalid size.
- Data chunk is missing.
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)