{"record":{"id":"237b4f2703287706","repo":"stride3d/stride","slug":"streamoutput-append-only-accepts-streams-or-output-objects","errorCode":null,"errorMessage":"StreamOutput.Append() only accepts Streams or Output objects","messagePattern":"StreamOutput\\.Append\\(\\) only accepts Streams or Output objects","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs","lineNumber":1064,"sourceCode":"                        if (compiler == null)\n                        {\n                            ((MethodCall)accessor).ProcessParameterSymbols(table, null);\n                            accessor.Type = ScalarType.Void;\n                            break;\n                        }\n\n                        var (builder, context) = compiler;\n\n                        // Emit OpAccessChain with everything so far\n                        EmitOpAccessChain(accessChainIds, i - 1);\n\n                        var output = methodCall.Arguments.Values[0].CompileAsValue(table, compiler);\n                        var streamsType = (StreamsType)methodCall.Arguments.Values[0].ValueType!;\n                        // Note: if it was a Streams, implicit cast it to Output\n                        if (streamsType.Kind == StreamsKindSDSL.Streams)\n                            output = new(builder.InsertData(new OpCopyLogical(context.GetOrRegister(new StreamsType(StreamsKindSDSL.Output)), context.Bound++, output.Id)));\n                        else if (streamsType.Kind == StreamsKindSDSL.Input)\n                            throw new InvalidOperationException(\"StreamOutput.Append() only accepts Streams or Output objects\");\n                        builder.Insert(new OpEmitVertexSDSL(output.Id));\n                        result = default;\n                        break;\n                    }\n                case (PointerType { BaseType: GeometryStreamType geometryStreamOutput },\n                    MethodCall { Name.Name: \"RestartStrip\", Arguments.Values.Count: 0 }):\n                    if (compiler == null)\n                    {\n                        ((MethodCall)accessor).ProcessParameterSymbols(table, null);\n                        accessor.Type = ScalarType.Void;\n                        break;\n                    }\n\n                    // Emit OpAccessChain with everything so far\n                    EmitOpAccessChain(accessChainIds, i - 1);\n\n                    compiler.Builder.Insert(new OpEndPrimitive());\n                    result = default;","sourceCodeStart":1046,"sourceCodeEnd":1082,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs#L1046-L1082","documentation":"In geometry-shader stream output handling, StreamOutput.Append(value) compiles its argument and inspects its StreamsType kind. Only Streams and Output kinds are valid; if the argument's stream type kind is Input, the compiler throws this InvalidOperationException. Input streams cannot be appended to (they are read-only vertex inputs to the geometry stage).","triggerScenarios":"Calling streamsOutput.Append(x) where x's ValueType is a StreamsType with Kind == StreamsKindSDSL.Input — i.e. appending to or appending an input stream in a StreamOutput.Append call.","commonSituations":"Geometry shader authors accidentally appending to an input stream variable instead of the stage's output stream, or passing the wrong stream variable as the Append receiver/argument.","solutions":["Append to the geometry stage's output stream (Streams/Output kind), not to an input stream variable.","Check the stream variable declaration; input streams (stage inputs) are read-only and cannot be append targets.","Rename or restructure the shader so the value passed to Append comes from inputs but is appended to the output stream."],"exampleFix":"// before (geometry shader SDSL)\ninputStream.Append(position);   // appending to an Input stream\n\n// after\noutputStream.Append(position);  // append to the Streams/Output stream","handlingStrategy":"type-guard","validationCode":"// guard before emitting Append\nif (arg.ValueType is StreamsType st && st.Kind == StreamsKindSDSL.Input)\n    throw new InvalidOperationException(\"Cannot Append to an Input stream\");","typeGuard":"static bool IsAppendableStream(TypeBase t) => t is StreamsType st && st.Kind is StreamsKindSDSL.Streams or StreamsKindSDSL.Output;","tryCatchPattern":"try { CompileGeometryShader(gs); }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"StreamOutput.Append()\"))\n{\n    // re-target Append at the output stream\n}","preventionTips":["Always append to the geometry stage's declared output stream, never to stage inputs.","Name input and output stream variables distinctly (e.g. input vs outputStream).","Review Append call receivers/arguments when refactoring geometry shaders."],"tags":["shader","sdsl","geometry-shader","streams"],"backgroundTag":"invalid-argument-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}