microsoft/aspire · error · DistributedApplicationException

The filesystem spelling of

Error message

The filesystem spelling of '{originalPath}' could not be read from '{current}'.

What it means

ResolveFilesystemCasing walks the manifest path segment by segment to learn the true on-disk spelling (casing/normalization) of each name, because Dockerfiles must reference the exact filesystem spelling. When enumerating Directory.GetFileSystemEntries(current) fails with IOException or UnauthorizedAccessException, the library cannot continue and throws this DistributedApplicationException wrapping the underlying exception.

Solutions

  1. Verify the manifest path exists and every intermediate directory is readable before publishing (run Directory.GetFileSystemEntries yourself).
  2. Fix filesystem permissions (chmod/chown/ACLs) so the publishing process can list the directory.
  3. Recreate or remount the failing volume; check dmesg/disk health if IOException persists.
  4. Re-enable case sensitivity or confirm the directory content matches the configured manifest path exactly.

Example fix

// before
manifestPath = "/app/Src/main.rs"; // Src deleted or unreadable
// after
if (!Directory.Exists("/app/Src")) throw new DirectoryNotFoundException("Regenerate or restore /app/Src before publishing");
manifestPath = "/app/src/main.rs";
Defensive patterns

Strategy: validation

Validate before calling

var dir = Path.GetDirectoryName(manifestPath);
if (dir is null || !Directory.Exists(dir)) throw new InvalidOperationException($"Manifest directory '{dir}' does not exist");
_ = Directory.GetFileSystemEntries(dir); // throws IOException/UnauthorizedAccessException early if unreadable

Try / catch

try { GenerateManifestAsync(); }
catch (DistributedApplicationException ex) when (ex.InnerException is IOException or UnauthorizedAccessException) {
    logger.LogError(ex.InnerException, "Cannot read filesystem casing for manifest path");
}

Prevention

When it happens

Trigger: Calling ValidateManifestPath (via publish-mode Dockerfile generation for a Rust app) with a manifest path whose directory cannot be enumerated: an intermediate directory was deleted between validation and enumeration, permissions deny listing, or an IO error occurs on a network/slow volume.

Common situations: Manifest path points into a directory removed or renamed after configuration; restricted ACLs on a CI container; a volume that returns IO errors (unmounted network share, failing disk).

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 microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/64a113ac9753d9fc. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Rust/RustDockerfileGenerator.cs:299

                for (; i < segments.Length; i++)
                {
                    current = Path.Combine(current, segments[i]);
                }

                return current;
            }

            // On a case-insensitive host, File.Exists accepts a spelling that will not exist after Docker
            // copies the context into Linux. Enumerating the parent returns the directory entry's stored
            // casing; on case-sensitive hosts the exact candidate above is the only matching entry.
            string[] entries;
            try
            {
                entries = Directory.GetFileSystemEntries(current);
            }
            catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
            {
                throw new DistributedApplicationException(
                    $"The filesystem spelling of '{originalPath}' could not be read from '{current}'.",
                    ex);
            }

            // APFS can treat canonically equivalent Unicode names as the same entry while returning the
            // stored normalization form. Normalize only for matching, then keep the enumerated spelling.
            var normalizedSegment = segments[i].Normalize(NormalizationForm.FormC);
            current = entries.FirstOrDefault(entry =>
                    string.Equals(Path.GetFileName(entry), segments[i], StringComparison.Ordinal))
                ?? entries.FirstOrDefault(entry =>
                    string.Equals(
                        Path.GetFileName(entry).Normalize(NormalizationForm.FormC),
                        normalizedSegment,
                        StringComparison.OrdinalIgnoreCase))
                ?? throw new DistributedApplicationException(
                    $"The filesystem spelling of '{originalPath}' could not be matched in '{current}'.");
        }

View on GitHub (pinned to 25830f84bd)