stride3d/stride · error · InvalidOperationException

Use of undeclared stream variable

Error message

Use of undeclared stream variable '{streamVar.Name}'

What it means

When a stream pointer is indexed with an identifier (e.g. stream var access during type processing, compiler == null), the compiler lets the identifier resolve stream variables. If after ProcessSymbol the identifier's type is not a PointerType, the symbol wasn't a declared stream variable, so this error is thrown. It indicates a reference to a name that is not a known stream in the stream-access chain.

Solutions

  1. Check spelling of the stream variable name against the declared streams in the shader signature.
  2. Ensure the stream variable is declared (as a stream/in/out with StreamsType) before it is accessed.
  3. Verify you are in a geometry-stream stage context where stream variables are valid.

Example fix

// before
ouputStream.Append(v);          // typo, undeclared

// after
outputStream.Append(v);         // matches the declared stream variable
Defensive patterns

Strategy: validation

Validate before calling

// check the identifier refers to a declared stream before accessing
var sym = table.TryLookup(streamName);
if (sym?.Type is not PointerType)
    throw new InvalidOperationException($"'{streamName}' is not a declared stream variable");

Type guard

static bool IsStreamVariable(Symbol s) => s?.Type is PointerType { BaseType: StreamsType };

Try / catch

try { CompileShader(source); }
catch (InvalidOperationException e) when (e.Message.StartsWith("Use of undeclared stream variable"))
{
    // fix the stream name or declare the stream
}

Prevention

When it happens

Trigger: Indexing a StreamsType pointer with an Identifier whose ProcessSymbol resolution yields a non-pointer type — i.e. the name does not refer to a declared stream variable.

Common situations: Typos in stream variable names inside geometry shader stream-access expressions, referring to a stream before its declaration, or accessing a stream from a stage/effect where it was never declared as a stream.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs:1129

                        }

                        var (builder, context) = compiler;
                        var importedVariable = ShaderDefinition.ImportSymbol(table, context, field.ResolvedSymbol!);

                        // Emit OpAccessChain with everything so far
                        EmitOpAccessChain(accessChainIds, i - 1);

                        // TODO: figure out instance (this vs composition)
                        result = IdentifierBase.EmitSymbol(builder, context, importedVariable, false, result.Id);
                        break;
                    }
                case (PointerType { BaseType: StreamsType s } p, Identifier streamVar):
                    if (compiler == null)
                    {
                        streamVar.AllowStreamVariables = true;
                        streamVar.ProcessSymbol(table);
                        if (streamVar.Type is not PointerType)
                            throw new InvalidOperationException($"Use of undeclared stream variable '{streamVar.Name}'");
                        accessor.Type = (PointerType)streamVar.Type with { StorageClass = p.StorageClass };
                        break;
                    }

                    // We emit chain access so far
                    // It's not necessary for SPIR-V, but StreamAccessPatcher.PatchStreamsAccesses() expect a simple format without
                    // multiple access chained (which make it harder to compute type)
                    EmitOpAccessChain(accessChainIds, i - 1);

                    // Since we cheated a bit by overwriting the accessor.Type, set it back during Compile()
                    accessor.Type = (PointerType)accessor.Type! with { StorageClass = Specification.StorageClass.Private };
                    // Since STREAMS struct is built later for each shader, we simply make a reference to variable for now
                    var streamVariableResult = streamVar.Compile(table, compiler);
                    accessor.Type = (PointerType)accessor.Type! with { StorageClass = p.StorageClass };
                    PushAccessChainId(accessChainIds, streamVariableResult.Id);

                    // For same reason as before (we want easy to detect pattern for StreamAccessPatcher), emit again
                    currentValueType = accessor.Type!;

View on GitHub (pinned to 96fad776d2)