stride3d/stride · error · InvalidOperationException
Asset returned by CreateAssets has no location. Use…
Error message
Asset returned by CreateAssets has no location. Use GenerateLocation to generate the location for each asset.
What it means
AssetTemplateGenerator.Run adds each asset produced by CreateAssets to the target package and requires every AssetItem to have a non-empty Location so unique names can be computed. An asset with an empty Location indicates the generator forgot to call GenerateLocation, so Run throws InvalidOperationException.
Solutions
- In CreateAssets, wrap each created asset with GenerateLocation(parameters) before yielding the AssetItem
- Ensure the generated location string is non-empty (factory created a valid asset with a default name)
- Inspect your custom generator's CreateAssets override; the base generator already calls GenerateLocation, so avoid bypassing it
Example fix
// before yield return new AssetItem(string.Empty, asset); // after yield return new AssetItem(GenerateLocation(parameters), asset);
Defensive patterns
Strategy: validation
Validate before calling
foreach (var item in CreateAssets(parameters))
if (string.IsNullOrEmpty(item.Location)) throw new InvalidOperationException("Asset item has no location"); Type guard
bool HasLocation(AssetItem a) => !string.IsNullOrEmpty(a.Location);
Try / catch
try { generator.Run(parameters, assets); }
catch (InvalidOperationException) { // surface 'missing location' as a template-authoring bug
} Prevention
- Always route asset creation through GenerateLocation(parameters) in custom generators
- Add a unit test that applies every custom template and asserts non-empty locations
- Never construct AssetItem directly with a hand-written location in generators
When it happens
Trigger: A custom AssetTemplateGenerator overrides CreateAssets and returns AssetItem instances constructed without a location (e.g. new AssetItem(asset) with empty string/null location) instead of using GenerateLocation(parameters).
Common situations: Third-party template generator implementations copied from outdated samples; refactoring CreateAssets to build AssetItems directly instead of through the base-class helper.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Unable to find the asset factory associated to this…
- An IMemberNode was expected when processing the path
- An IObjectNode was expected when processing the path
- An item has been added to a collection that does not have a…
- Custom strides is not supported with packed PixelFormats
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/1376060a2b7f88cc.
Report an issue: GitHub.
Appendix: source
Thrown at sources/editor/Stride.Assets.Presentation/Templates/AssetTemplateGenerator.cs:44
parameters.Validate();
return PrepareAssetCreation(parameters);
}
public sealed override bool Run(AssetTemplateGeneratorParameters parameters)
{
var assets = CreateAssets(parameters)?.ToList();
if (assets == null)
{
parameters.Logger.Error("No asset created by the asset factory.");
return false;
}
// Step one: add assets to package with proper unique name
var newAssets = new Dictionary<AssetId, AssetItem>();
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.Location))
throw new InvalidOperationException($"Asset returned by {nameof(CreateAssets)} has no location. Use {nameof(GenerateLocation)} to generate the location for each asset.");
// Ensure unicity of names amongst package
var name = NamingHelper.ComputeNewName(asset.Location, x => parameters.Package.Assets.Find(x) != null, "{0}_{1}");
var item = new AssetItem(name, asset.Asset);
try
{
parameters.Package.Assets.Add(item);
// Ensure the dirty flag is properly set to refresh the dependency manager
item.IsDirty = true;
}
catch (Exception ex)
{
parameters.Logger.Error("Failed to create new asset from template.", ex);
return false;
}
newAssets.Add(item.Id, item);
}View on GitHub (pinned to 96fad776d2)