Skip to content

VerificationException on runtime, due to upcast to obj inside the task state machine #16154

Description

@Thorium

There I was, minding my own business, of writing generic higher order functions in asynchronous code, when she hit me, the task-state-machine. She has hit me before, but this time I think she left some traces of her, so I could saw a glimpse of that monster. So I'll report it here, even this will probably be closed "as designed", it might help some other guy struggling in seduction by her.

I haven't been able to repro it in short code, yet, but this is what is going on.
Observe the following code:

open System.Linq
type Shape = {x: int; y: int}

let returnFilter condition =
    task {
        if condition = "a" then
            let filter1 : IQueryable<Shape> -> IQueryable<Shape> =
                fun data -> data.Where(fun a -> a.x = 1)
            return Some filter1
        elif condition = "b" then
            let filter2 : IQueryable<Shape> -> IQueryable<Shape> =
                fun data -> data.Where(fun a -> a.y <> 2)
            return Some filter2
        else
            return None
    }

...and then composing these filters back and forth, e.g.

    let data = [{x = 1; y = 1}; {x = 2; y = 2}].AsQueryable()
    task {
        let! getfilter1 = returnFilter "a"
        let! getfilter2 = returnFilter "b"
        match getfilter1, getfilter2 with
        | Some filter1, Some filter2 ->
            return data |> filter2 |> filter1 |> Seq.toList
        // ...and so on...

Expected behavior

Silly me, by the code I expected the type of returnFilter is:
string -> Task<(IQueryable<Shape> -> IQueryable<Shape>) option>

Actual behavior

Well, the actual type of returnFilter is:
string -> Task<(#IQueryable<Shape> -> IQueryable<Shape>) option>

The code compiled well.
But this hash # made all the difference, because combining different returnFilters together caused them to be automatically upcasted to obj.

Thus the code threw at runtime:

System.Security.VerificationException: Method returnFilter: type argument 'System.Object' violates the constraint of type parameter 'a'.
   at ...(my code location of "task{")...
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)

What I historically do is change the code from task to async and all is good, but not this time:

System.Security.VerificationException: Method returnFilter: type argument 'System.Object' violates the constraint of type parameter 'a'.
    at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in D:\\a\\_work\\1\\s\\src\\FSharp.Core\\async.fs:line 510
   at ...(mycode)...
   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\\a\\_work\\1\\s\\src\\FSharp.Core\\async.fs:line 114
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at ...(mycode)...

Known workarounds

Do write the parameters explicitly.
You can even declare a type for your function:

type ShapeFilter = IQueryable<Shape> -> IQueryable<Shape>
let returnFilter condition : Task<Option<ShapeFilter>>=
   //... and now the compiler is happy, and the runtime is happy.

Related information

Provide any related information (optional):

  • Operating system Windows 11
  • .NET Runtime kind: dotnet 8.0.100-rc.1.23463.5, .NET 4.8.09032
  • Editing Tools Visual Studio 2022 Version 17.7.4

Activity

  1. added this to the Backlog milestone on Oct 22, 2023
  2. changed the title [-]VerificationException on runtime due to upcast of inside task state machine[/-] [+]VerificationException on runtime, due to upcast to obj inside the task state machine[/+] on Oct 22, 2023
  3. added
    Area-Compiler-StateMachinesSequence, list, task and other state machine compilation
    Impact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.
    and removed on Oct 23, 2023
  4. T-Gro commented on Oct 23, 2023

    @T-Gro
    Member

    (to be verified if/since-when this is a regression)

  5. github-actions commented on Mar 28, 2026

    @github-actions
    Contributor

    🤖 This is an automated response from Repo Assist.

    I tested this with the current compiler (F# Interactive version 15.2.100.0 for F# 11.0, commit a0cce49, .NET 10.0.5 on Linux):

    $ echo '#version;;' | fsi
    Microsoft (R) F# Interactive version 15.2.100.0 for F# 11.0
    Language Version: 10.0
    FSharp.Core: 11.0.0.0
    .NET: .NET 10.0.5
    

    The repro code compiles and runs correctly:

    open System.Linq
    type Shape = {x: int; y: int}
    
    let returnFilter condition =
        task {
            if condition = "a" then
                let filter1 : IQueryable<Shape> -> IQueryable<Shape> =
                    fun data -> data.Where(fun a -> a.x = 1)
                return Some filter1
            elif condition = "b" then
                let filter2 : IQueryable<Shape> -> IQueryable<Shape> =
                    fun data -> data.Where(fun a -> a.y <> 2)
                return Some filter2
            else
                return None
        }
    
    let data = [{x = 1; y = 1}; {x = 2; y = 2}].AsQueryable()
    let result = 
        task {
            let! f1 = returnFilter "a"
            let! f2 = returnFilter "b"
            match f1, f2 with
            | Some filter1, Some filter2 ->
                return data |> filter2 |> filter1 |> Seq.toList
            | _ -> return []
        } |> (fun t -> t.GetAwaiter().GetResult())
    // Result: [{ x = 1; y = 1 }]  ✅

    The returnFilter function is now correctly inferred as string -> Task<(IQueryable<Shape> -> IQueryable<Shape>) option> (no longer #IQueryable), and no VerificationException is thrown at runtime. The task filter composition works correctly, returning the expected filtered result.

    Generated by Repo Assist · ◷

    To install this agentic workflow, run

    gh aw add githubnext/agentics/workflows/repo-assist.md@9135cdfde26838a01779aa966628308404ec1f02
    
  6. added a commit that references this issue on Mar 31, 2026
  7. github-actions commented on Mar 31, 2026

    @github-actions
    Contributor

    🤖 Issue still reproduces. Regression test in #19530 (PR) confirms it on Desktop .NET Framework. Still errors with: VerificationException: type argument 'System.Object' violates the constraint of type parameter 'a'. Apologies for the incorrect label.

    Generated by Regression PR Shepherd · ◷

  8. added a commit that references this issue on Apr 10, 2026
  9. reopened this on Apr 10, 2026
  10. T-Gro commented on Apr 10, 2026

    @T-Gro
    Member

    This now only happens in .NET Framework due to stricter IL verification. NET10 passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-Compiler-StateMachinesSequence, list, task and other state machine compilationBugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.Regression

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions