stride3d/stride · error · InvalidOperationException

assetItem must be an absolute path

Error message

assetItem must be an absolute path

What it means

AssetCompilerBase.Prepare derives the compilation output path from the asset item's FullPath, which must be absolute to be deterministic and resolvable on disk. If the passed AssetItem's FullPath is relative, it throws InvalidOperationException because relative paths cannot be used to locate/create compiler outputs.

Solutions

  1. Resolve the asset path against the package/project root to an absolute UFile before creating the AssetItem.
  2. Check fullPath.IsAbsolute yourself and convert with UPath.Combine(baseDir, relativePath) before calling Prepare.
  3. If you only have a relative path, obtain the containing Package's root directory and combine it with the asset location.
  4. Fix the upstream code that produced the relative path (e.g. missing Package root in a session/package container).

Example fix

// before
var item = new AssetItem(new UFile("assets/my/logo.sdtasy"), descriptor);
compiler.Prepare(context, item);
// after
var abs = UPath.Combine("/path/to/project", "assets/my/logo.sdtasy");
var item = new AssetItem(new UFile(abs), descriptor);
compiler.Prepare(context, item);
Defensive patterns

Strategy: validation

Validate before calling

if (!assetItem.FullPath.IsAbsolute)
    assetItem.FullPath = UPath.Combine(packageRootDirectory, assetItem.FullPath);

Type guard

static bool HasAbsolutePath(AssetItem item) => item?.FullPath != null && item.FullPath.IsAbsolute;

Try / catch

try
{
    compiler.Prepare(context, assetItem);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("absolute path"))
{
    logger.Error($"Asset {assetItem?.Location} has a relative path; resolve it against the package root first.");
}

Prevention

When it happens

Trigger: Calling Prepare (directly or through AssetCompilerRegistry/asset compilation pipeline) with an AssetItem constructed from a relative UFile path, e.g. new AssetItem("myasset", descriptor) where the source path was never rooted against a package/project directory.

Common situations: Building assets outside the normal package pipeline where paths were not resolved via package root; custom tooling constructing AssetItem from command-line relative paths; tests creating AssetItems without an absolute source path.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/72355512c69170db. Report an issue: GitHub.

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/Compiler/AssetCompilerBase.cs:55

    {
        yield break;
    }

    public AssetCompilerResult Prepare(AssetCompilerContext context, AssetItem assetItem)
    {
        ArgumentNullException.ThrowIfNull(context);
        ArgumentNullException.ThrowIfNull(assetItem);

        var result = new AssetCompilerResult(GetType().Name)
        {
            BuildSteps = new AssetBuildStep(assetItem)
        };

        // Only use the path to the asset without its extension
        var fullPath = assetItem.FullPath;
        if (!fullPath.IsAbsolute)
        {
            throw new InvalidOperationException("assetItem must be an absolute path");
        }

        // Try to compile only if we're sure that the sources exist.
        if (EnsureSourcesExist(result, assetItem))
        {
            Prepare(context, assetItem, assetItem.Location.GetDirectoryAndFileName(), result);
        }

        return result;
    }

    /// <summary>
    /// Compiles the asset from the specified package.
    /// </summary>
    /// <param name="context">The context to use to compile the asset.</param>
    /// <param name="assetItem">The asset to compile</param>
    /// <param name="targetUrlInStorage">The absolute URL to the asset, relative to the storage.</param>
    /// <param name="result">The result where the commands and logs should be output.</param>

View on GitHub (pinned to 96fad776d2)