stride3d/stride · error · FileNotFoundException

Could not find executable to start assembly

Error message

Could not find executable to start assembly [{assemblyLocation}]

What it means

LoaderToolLocator.GetExecutable searches a set of candidate directories for the executable that corresponds to a given managed assembly location and returns the first path that exists. When none of the candidates exist on disk, it gives up with a FileNotFoundException naming the assembly. This signals that the tool's companion native/host executable is missing or not deployed alongside the assembly.

Solutions

  1. Verify the companion executable exists next to the assembly (same directory or expected subfolder) and restore/copy it.
  2. Rebuild or republish the tool so the host executable is included in the output (dotnet publish with the correct RID).
  3. Pass the correct assemblyLocation path that points at the deployed output, not a stale or moved location.
  4. Check that no antivirus/cleanup step deleted the executable after deployment.

Example fix

// before
copy bin\Debug\MyTool.dll deploy\
// after
dotnet publish MyTool -c Release -r win-x64 -o deploy\   # publishes exe + native loaders together
Defensive patterns

Strategy: try-catch

Validate before calling

var exeDir = Path.GetDirectoryName(assemblyLocation);
if (exeDir == null || !Directory.GetFiles(exeDir, "*").Any(f => f.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)))
    throw new InvalidOperationException($"Deployment directory {exeDir} looks incomplete; refusing to locate loader tool.");

Type guard

static bool IsDeployedToolPresent(string assemblyLocation) =>
    File.Exists(assemblyLocation) &&
    Directory.EnumerateFiles(Path.GetDirectoryName(assemblyLocation)!, "*")
        .Any(f => f.EndsWith(".exe", StringComparison.OrdinalIgnoreCase));

Try / catch

try
{
    var exe = LoaderToolLocator.GetExecutable(assemblyLocation);
}
catch (FileNotFoundException ex) when (ex.Message.Contains("Could not find executable to start assembly"))
{
    logger.Error($"Tool executable missing for {assemblyLocation}; republish with 'dotnet publish -r <RID>'.");
    throw;
}

Prevention

When it happens

Trigger: Calling GetExecutable(assemblyLocation) when File.Exists() fails for every candidate path built from the assembly location (e.g. wrong build output directory, exe not copied to output, wrong runtime identifier folder).

Common situations: Running a tool from a partial publish output; moving the assembly exe away from its native loaders; switching RID/platform folders (e.g. win-x64 vs linux-x64) without redeploying; copying just the .dll without the .exe.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at sources/shared/LoaderToolLocator/LoaderToolLocator.cs:60

            var tfmPath = Path.GetDirectoryName(assemblyLocation);
            if (tfmPath != null)
            {
                var parentPath = Path.GetDirectoryName(tfmPath);
                if (parentPath != null && Path.GetFileName(parentPath).Equals("lib", StringComparison.Ordinal))
                {
                    var packageRoot = Path.GetDirectoryName(parentPath);
                    if (packageRoot != null)
                    {
                        var tfm = Path.GetFileName(tfmPath);
                        var exeName = Path.GetFileNameWithoutExtension(assemblyLocation) + ExeExtension;
                        var exeLocation = Path.Combine(packageRoot, "tools", tfm, exeName);
                        if (File.Exists(exeLocation))
                            return exeLocation;
                    }
                }
            }

            throw new FileNotFoundException($"Could not find executable to start assembly [{assemblyLocation}]");
        }
    }
}

View on GitHub (pinned to 96fad776d2)