stride3d/stride · error · InvalidOperationException

Relative path is not allowed in FileSystemProvider.

Error message

Relative path is not allowed in FileSystemProvider.

What it means

OpenStream rejects URLs containing '..' path segments when a local base path is set, because they could escape the provider's root directory. This is a deliberate path-traversal guard, not a file-system error.

Solutions

  1. Resolve and normalize the URL to an absolute path before passing it to OpenStream
  2. Sanitize user input: strip or reject '..' segments
  3. Restructure code so files outside the base are accessed via a different provider, not traversal

Example fix

// before
var stream = provider.OpenStream($"{userInput}", VirtualFileMode.Open, VirtualFileAccess.Read);
// after
var safe = Path.GetFullPath(Path.Combine(basePath, userInput));
if (!safe.StartsWith(basePath)) throw new UnauthorizedAccessException();
var stream = provider.OpenStream(safe, VirtualFileMode.Open, VirtualFileAccess.Read);
Defensive patterns

Strategy: validation

Validate before calling

bool isSafe = !url.Split('/', '\\').Contains("..");
if (!isSafe) throw new ArgumentException("Path traversal detected");

Try / catch

try { stream = provider.OpenStream(url, mode, access); }
catch (InvalidOperationException) { throw new UnauthorizedAccessException($"Rejected path: {url}"); }

Prevention

When it happens

Trigger: Calling OpenStream with a URL like "../secrets.txt" or "assets/../../etc/passwd" (on any separator style), while the provider has a non-null localBasePath.

Common situations: User-supplied filenames being opened directly, URLs joined from untrusted input, or legitimate relative paths that were never resolved to absolute form first.

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 stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/4f805b15c16c175f. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.IO/FileSystemProvider.cs:158

        end = -1;
        return true;
    }

    /// <inheritdoc/>
    public override string[] ListFiles(string url, string searchPattern, VirtualSearchOption searchOption)
    {
        return Directory.GetFiles(ConvertUrlToFullPath(url), searchPattern, (SearchOption)searchOption).Select(ConvertFullPathToUrl).ToArray();
    }

#if STRIDE_PLATFORM_IOS
        public bool AutoSetSkipBackupAttribute { get; set; }
#endif

    /// <inheritdoc/>
    public override Stream OpenStream(string url, VirtualFileMode mode, VirtualFileAccess access, VirtualFileShare share = VirtualFileShare.Read, StreamFlags streamType = StreamFlags.None)
    {
        if (localBasePath != null && url.Split(VirtualFileSystem.DirectorySeparatorChar, VirtualFileSystem.AltDirectorySeparatorChar).Contains(".."))
            throw new InvalidOperationException("Relative path is not allowed in FileSystemProvider.");
        var filename = ConvertUrlToFullPath(url);
        var result = new FileStream(filename, (FileMode)mode, (FileAccess)access, (FileShare)share);

#if STRIDE_PLATFORM_IOS
            if (AutoSetSkipBackupAttribute && (mode == VirtualFileMode.CreateNew || mode == VirtualFileMode.Create || mode == VirtualFileMode.OpenOrCreate))
            {
                Foundation.NSFileManager.SetSkipBackupAttribute(filename, true);
            }
#endif

        return result;
    }

    public override DateTime GetLastWriteTime(string url)
    {
        return File.GetLastWriteTime(ConvertUrlToFullPath(url));
    }
}

View on GitHub (pinned to 96fad776d2)