stride3d/stride · error · InvalidOperationException
could not resolve symbol
Error message
{this} could not resolve symbol What it means
Thrown by CompileSymbol when the AST node's ResolvedSymbol property is null, i.e. symbol resolution (name binding) failed but the node still reached SPIR-V emission. The compiler cannot emit code for an identifier it could not bind to a declared symbol.
Solutions
- Fix the shader source so the identifier refers to a declared, in-scope symbol
- Ensure symbol resolution runs (and reports errors) before SPIR-V compilation
- Check inheritance/mixin setup so referenced stage variables are actually imported
Example fix
// before float c = undeclaredVar; // ResolvedSymbol stays null // after float declaredVar = 1.0; float c = declaredVar;
Defensive patterns
Strategy: validation
Validate before calling
if (node.ResolvedSymbol is null)
table.AddError(new(node.Info, SDSLErrorMessages.SDSL0110)); // report instead of emitting Type guard
bool IsResolved(ShaderLiteral node) => node.ResolvedSymbol is not null;
Try / catch
try { node.CompileSymbol(table, builder, context, constantOnly); }
catch (InvalidOperationException ex) { table.AddError(new(node.Info, ex.Message)); } Prevention
- Run full symbol resolution before SPIR-V generation
- Report unresolved identifiers as compile errors at resolution time
- Check shader inheritance imports cover all referenced names
When it happens
Trigger: Compiling a ShaderLiteral/identifier node whose name did not match any declaration in the SymbolTable, so the earlier resolution pass left ResolvedSymbol null; calling CompileSymbol directly without running resolution.
Common situations: Typos in variable/function names in SDSL code; referencing symbols from a mixin/inherit chain that was not fully imported; using a variable before its declaration; SDSL0110-style lookup failures silently ignored upstream.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Unsupported symbol type
- Member not found
- Could not resolve type
- Unsupported StreamsKindSDSL for output parameter
- Could not compile shader. See error messages:
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/234b9aeeabfc435c.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Literals.cs:440
base.SetValue(table, compiler, rvalue);
return;
}
rvalue = builder.Convert(context, rvalue, ((PointerType)Type).BaseType);
builder.Insert(new OpStore(target.Id, rvalue.Id, null, []));
}
public override SpirvValue CompileImpl(SymbolTable table, CompilerUnit compiler)
{
var (builder, context) = compiler;
return CompileSymbol(table, builder, context, builder.CurrentFunction == null);
}
protected virtual SpirvValue CompileSymbol(SymbolTable table, SpirvBuilder builder, SpirvContext context, bool constantOnly)
{
if (ResolvedSymbol is null)
throw new InvalidOperationException($"{this} could not resolve symbol");
var symbol = ShaderDefinition.ImportSymbol(table, context, ResolvedSymbol);
// Track when a stage method accesses a non-stage variable (without composition qualifier).
// This forces the shader to be fully imported at root level instead of stage-only during mixin.
if (symbol.MemberAccessWithImplicitThis != null && !symbol.Id.IsStage && builder.CurrentFunction is { IsStage: true })
{
var varOwner = symbol.OwnerType;
if (varOwner != null && varOwner != table.CurrentShader)
{
foreach (var inst in context)
{
if (inst.Op == Spirv.Specification.Op.OpMixinInheritSDSL && (OpMixinInheritSDSL)inst is { } inherit
&& table.ResolveShader(inherit.Shader) is { } lss && lss.Name == varOwner.Name)
{
inherit.Flags |= Spirv.Specification.MixinInheritFlagsMask.NeedsFullImport;
break;
}View on GitHub (pinned to 96fad776d2)