stride3d/stride · error · InvalidOperationException
No function termination, but a return value is expected
Error message
No function termination, but a return value is expected
What it means
EndFunction auto-inserts a Return when the last block isn't terminated. If the function's return type is non-void, no value-less return can be synthesized, so it throws InvalidOperationException: the function body ended without returning the required value.
Solutions
- Ensure every execution path in the function ends with Return(value) for non-void functions.
- Add a final Return(defaultValue) after branch merges as a fallback.
- Make helper-generated control flow always terminate each branch with a return.
- Verify with SPIR-V validation (spirv-val) during development to catch missing terminators early.
Example fix
// before
if (cond) { Return(a); } else { Branch(elseBlock); } // falls off the end
// after
if (cond) { Return(a); } else { Return(b); } // or: merge block ends with Return(result) Defensive patterns
Strategy: validation
Validate before calling
if (functionType.ReturnType != ScalarType.Void) ensureAllPathsReturn(bodyBlocks);
Type guard
bool needsExplicitReturn = fn.FunctionType.ReturnType != ScalarType.Void;
Try / catch
try { builder.EndFunction(); } catch (InvalidOperationException ex) when (ex.Message.Contains("return value")) { /* append a fallback Return(value) before retrying */ } Prevention
- Terminate every control-flow path with Return for non-void functions
- Add a final fallback Return after branch merges
- Run spirv-val on generated modules in CI
- Check discard/clip helpers don't skip the return path
When it happens
Trigger: Emitting a non-void function whose control flow reaches OpFunctionEnd without an OpReturnValue/OpReturn — e.g. all branches returned but a trailing path (or a missed branch) falls through, or Return(value) was never emitted on some path.
Common situations: Shader functions with if/else where one branch omits the return, early code-generated functions with dynamic branches (discard/clip) that skip the final return, or translation bugs from HLSL where all paths were assumed to return.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Binary operator not supported for element type
- Can't cast between array of different sizes
- Can't cast between array of different types
- Can't cast between structures
- Can't cast from to (more components)
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/35185d8c50d30de9.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Functions.cs:39
public void BeginFunction(SpirvContext context, SpirvFunction function, FunctionControlMask mask = FunctionControlMask.None)
{
Buffer.Add(new OpFunction(context.GetOrRegister(function.FunctionType.ReturnType), function.Id, mask, context.GetOrRegister(function.FunctionType)));
Position = Buffer.Count;
CurrentFunction = function;
}
public void EndFunction()
{
if (CurrentFunction is null)
throw new InvalidOperationException("Trying to end a function which was not started");
// If there was no explicit return, add one
var lastInstruction = Buffer[Position - 1];
if (!IsBlockTermination(lastInstruction.Op))
{
if (CurrentFunction.Value.FunctionType.ReturnType != ScalarType.Void)
throw new InvalidOperationException("No function termination, but a return value is expected");
Return(null);
}
Buffer.Insert(Position++, new OpFunctionEnd());
}
public SpirvValue EmitFunctionParameter(SpirvContext context, string name, SymbolType type)
{
var p = Buffer.Insert(Position++, new OpFunctionParameter(context.GetOrRegister(type), context.Bound++));
context.AddName(p, name);
var value = new SpirvValue(p.ResultId, p.ResultType, name);
CurrentFunction!.Value.Parameters.Add(name, value);
return value;
}
public static OpFunctionParameter GetFunctionParameter(SpirvBuffer buffer, Symbol method, int functionParameterIndex)
{View on GitHub (pinned to 96fad776d2)