microsoft/aspire · error · DistributedApplicationException

The Rust app ' ' builds from ' ', which resolves outside…

Error message

The Rust app '{resourceName}' builds from '{manifestPath}', which resolves outside its app directory '{workingDirectory}'. Only the app directory is copied into the image.

What it means

ValidateManifestPath computes the relative path from the canonicalized app directory to the canonicalized manifest and throws this DistributedApplicationException if it escapes that directory ('..', a rooted result, or a '..'/'..' + separator prefix). Only the app directory is copied into the Docker image as build context, so a manifest outside it can never be included in the generated Dockerfile — publishing stops with an explicit message instead.

Solutions

  1. Make the resource's app directory the nearest ancestor that actually contains Cargo.toml so the manifest is inside the build context
  2. Copy or vendor the needed crate into the app directory instead of referencing it via '../'
  3. Restructure the workspace so the published Rust app and its manifest live under one directory that becomes the image build context

Example fix

// before
builder.AddRustApp("app", "../shared/Cargo.toml"); // outside the app dir
// after
builder.AddRustApp("app", "../shared"); // app dir that contains Cargo.toml
Defensive patterns

Strategy: validation

Validate before calling

var rel = Path.GetRelativePath(Path.GetFullPath(appDir), Path.GetFullPath(manifestPath, appDir));
if (Path.IsPathRooted(rel) || rel.StartsWith("..")) throw new InvalidOperationException("Manifest must resolve inside the app directory.");

Type guard

bool ManifestInsideAppDir(string dir, string manifest) { var rel = Path.GetRelativePath(Path.GetFullPath(dir), Path.GetFullPath(manifest, dir)); return !Path.IsPathRooted(rel) && !rel.StartsWith(".."); }

Try / catch

try { await PublishAsync(appModel); } catch (DistributedApplicationException ex) when (ex.Message.Contains("outside its app directory")) { log.LogError(ex, "Cargo manifest outside build context"); }

Prevention

When it happens

Trigger: Publishing a RustAppResource whose Cargo.toml, after symlink resolution, resolves to a location outside workingDirectory — e.g. the manifest path contains '../..' segments or the app directory is a symlink into a sibling tree so the canonical manifest lands outside the canonical context.

Common situations: Workspace layouts where the resource directory references a sibling crate via '../shared/Cargo.toml'; symlinked directories whose canonicalization moves the manifest outside the apparent app folder; pointing the resource at a parent directory while the manifest sits below it via traversal.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/85729123a911f0bc. Report an issue: GitHub.

Appendix: source

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

            || !PathNormalizer.TryResolveSymlinks(filesystemManifest, out var canonicalManifest))
        {
            throw new DistributedApplicationException(
                $"The Rust app '{resourceName}' builds from '{manifestPath}', but its symbolic links could not be " +
                "fully resolved. Publishing stops rather than accepting a partially canonicalized path.");
        }

        // Resolve aliases first so equivalent roots such as /var and /private/var become lexically related.
        // Then only enumerate entries below the build context to recover the spelling Docker will copy.
        canonicalManifest = ResolveFilesystemCasing(canonicalWorkingDirectory, canonicalManifest);

        var relativeManifest = Path.GetRelativePath(canonicalWorkingDirectory, canonicalManifest);

        if (Path.IsPathRooted(relativeManifest)
            || relativeManifest == ".."
            || relativeManifest.StartsWith($"..{Path.DirectorySeparatorChar}", StringComparison.Ordinal)
            || relativeManifest.StartsWith($"..{Path.AltDirectorySeparatorChar}", StringComparison.Ordinal))
        {
            throw new DistributedApplicationException(
                $"The Rust app '{resourceName}' builds from '{manifestPath}', which resolves outside its app directory " +
                $"'{workingDirectory}'. Only the app directory is copied into the image.");
        }

        // Rebase canonical-equivalent spellings (for example /var and /private/var on macOS) to the path
        // below the build context. A Unix backslash is a legal filename character, while a Windows backslash
        // is a host separator that must become a forward slash for the Linux container.
        return OperatingSystem.IsWindows() ? relativeManifest.Replace('\\', '/') : relativeManifest;
    }

    private static string ResolveFilesystemCasing(string workingDirectory, string path)
    {
        var originalPath = path;
        var relativePath = Path.GetRelativePath(workingDirectory, path);
        if (relativePath == ".")
        {
            return workingDirectory;
        }

View on GitHub (pinned to 25830f84bd)