CoplayDev/unity-mcp · error · InvalidOperationException

Screenshot folder '{folderOverride}' resolves outside the Un

Error message

Screenshot folder '{folderOverride}' resolves outside the Unity project root ('{fullFolder}'). Use a project-relative path (e.g. 'Assets/Screenshots' or 'Captures').

What it means

Thrown by ResolveFolderAbsolute when the resolved screenshot folder path escapes the Unity project root. This is a deliberate security guard against path traversal. The method computes the full path and checks it starts with the project root; anything outside (absolute paths elsewhere, or relative paths using '..' that traverse up) is rejected.

Source

Thrown at MCPForUnity/Runtime/Helpers/ScreenshotUtility.cs:654

            string projectRoot = GetProjectRootPath().TrimEnd('/');
            string requested = string.IsNullOrWhiteSpace(folderOverride) ? DefaultFolder : folderOverride.Trim();
            requested = requested.Replace('\\', '/').TrimEnd('/');

            string combined = Path.IsPathRooted(requested)
                ? requested
                : Path.Combine(projectRoot, requested);

            string fullFolder = Path.GetFullPath(combined).Replace('\\', '/').TrimEnd('/');
            string normalizedRoot = projectRoot;

            // Reject paths that escape the project root (case-insensitive on Windows, exact elsewhere).
            var rootComparison = Application.platform == RuntimePlatform.WindowsEditor
                ? StringComparison.OrdinalIgnoreCase
                : StringComparison.Ordinal;
            if (!fullFolder.Equals(normalizedRoot, rootComparison) &&
                !fullFolder.StartsWith(normalizedRoot + "/", rootComparison))
            {
                throw new InvalidOperationException(
                    $"Screenshot folder '{folderOverride}' resolves outside the Unity project root ('{fullFolder}'). " +
                    $"Use a project-relative path (e.g. 'Assets/Screenshots' or 'Captures').");
            }

            return fullFolder;
        }

        /// <summary>
        /// Converts an absolute filesystem path inside the project to a project-relative path
        /// (forward slashes, no leading separator). Returns the input unchanged when it does
        /// not live under the project root.
        /// </summary>
        public static string ToProjectRelativePath(string normalizedFullPath)
        {
            if (string.IsNullOrEmpty(normalizedFullPath)) return normalizedFullPath;
            string projectRoot = GetProjectRootPath();
            string normalized = normalizedFullPath.Replace('\\', '/');
            if (normalized.StartsWith(projectRoot, StringComparison.OrdinalIgnoreCase))

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Use a project-relative path like 'Assets/Screenshots' or 'Captures'.
  2. If you need screenshots outside the project, capture to a project folder then copy/move the file externally afterward.
  3. Avoid absolute paths and '..' traversal in folderOverride.
  4. Ensure the folder name does not contain backslashes on Windows that confuse path resolution (use forward slashes).

Example fix

// before
params = {"folderOverride": "/tmp/screenshots"}
// after
params = {"folderOverride": "Assets/Screenshots"}
Defensive patterns

Strategy: validation

Validate before calling

import os

def validate_folder_in_project(folder: str, project_root: str) -> bool:
    """Returns True if the resolved folder is within the project root."""
    if os.path.isabs(folder):
        combined = folder
    else:
        combined = os.path.join(project_root, folder)
    full = os.path.normpath(combined)
    root = os.path.normpath(project_root)
    return full == root or full.startswith(root + os.sep)

Try / catch

try
{
    string folder = ResolveFolderAbsolute(folderOverride);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("resolves outside the Unity project root"))
{
    return new ErrorResponse("Use a project-relative path like 'Assets/Screenshots'.");
}

Prevention

When it happens

Trigger: Passing a folderOverride with '../' sequences that resolve outside the project, an absolute path to a different directory (e.g. '/tmp' or 'C:\Users'), or a symlink that points outside the project root.

Common situations: AI passes an absolute temp path thinking it's convenient; user wants screenshots in a shared external folder; relative path with too many '..' levels; path normalization differences between platforms (symlinks, junction points).

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/377544a8a9d98753. Report an issue: GitHub.