Skip to content

Error: FSharp.Reflection: Microsoft.FSharp.Core.FSharpOption`1 is not an F# union type #4082

Closed

Description

@mlaily

Hello,

I encountered a surprising error while using Fable.Elmish.Browser custom Traces.console:

Unable to process the message: [*removed*] Error: Microsoft.FSharp.Core.FSharpOption`1 is not an F# union type
    getUnionCases Reflection.js:310
    getCaseName Program.fs:25
    ElmishDebug_getMsgNameAndFields Program.fs:47
    program_4 Program.fs:54
    update program.fs:92
    [...]

And indeed the type object has undefined cases.
Image

I verified in an F# interactive console and it does work as expected out of Fable:

> FSharp.Reflection.FSharpType.GetUnionCases(typeof<option<string>>);;
val it: Reflection.UnionCaseInfo array =
  [|FSharpOption`1.None
      {DeclaringType = Microsoft.FSharp.Core.FSharpOption`1[System.String];
       Name = "None";
       Tag = 0;};
    FSharpOption`1.Some
      {DeclaringType = Microsoft.FSharp.Core.FSharpOption`1[System.String];
       Name = "Some";
       Tag = 1;}|]

I added a unit test in Fable on my fork (see diff here) and it fails with the same error as above.

Image


Is it a known shortcoming of the reflection implementation in Fable that option types are not considered union cases?

Or is it a bug that needs fixing?

(I suspect it's a known shortcomings, because it seems too big to have been missed by inadvertence... but strangely I couldn't find an issue mentioning this?)

Activity

  1. changed the title [-]Error: FSharp.Reflection: `Microsoft.FSharp.Core.FSharpOption`1` is not an F# union type[/-] [+]Error: FSharp.Reflection: Microsoft.FSharp.Core.FSharpOption`1 is not an F# union type[/+] on Mar 16, 2025
  2. mlaily commented on Mar 16, 2025

    @mlaily
    ContributorAuthor

    Ah, actually the issue might not be where I though it was...

    I'm still not sure whether it's normal that an option is not seen as a DU in Fable compiled code, but the root cause of my initial error seems to be a discrepancy in the behaviour of Microsoft.FSharp.Reflection.FSharpType methods vs Fable.Core.Reflection methods, probably due to the erasure of options.

    The following code:

    type MyUnion = | A of int | B of string
    
    let testValue = Some (MyUnion.A 42)
    
    Browser.Dom.console.log ({| TestValue = testValue |})
    Browser.Dom.console.log ({| TestValueType = testValue.GetType() |})
    Browser.Dom.console.log ({| IsUnionFS = Microsoft.FSharp.Reflection.FSharpType.IsUnion(testValue.GetType())|})
    Browser.Dom.console.log ({| IsUnionFable = Fable.Core.Reflection.isUnion testValue |})
    Browser.Dom.console.log ({| CaseNames = Fable.Core.Reflection.getCaseName testValue |})
    Browser.Dom.console.log ({| CaseFields = Fable.Core.Reflection.getCaseFields testValue |})

    Displays the following in a console:

    Image

    The union case seen by Fable.Core.Reflection is actually not the option, but the wrapped value! (which is itself a custom DU)

    So mixing reflection calls from both namespaces at the same time seems very error prone...

    EDIT: this has been fixed here elmish/browser#77

    I'm still curious about your expert opinion on this:

    • Is all the above expected?
    • Is there something we could improve? In documentation at least?
    • What's your recommendation regarding reflection in Fable? Prefer using Microsoft.FSharp.Reflection or Fable.Core.Reflection?
  3. kerams commented on Mar 21, 2025

    @kerams
    Contributor

    Related #2110

  4. added a commit that references this issue on Apr 12, 2026
    6924ec4
  5. github-actions commented on Apr 12, 2026

    @github-actions
    Contributor

    Note

    The pull request was not created — a fallback review issue was created instead due to protected file changes: #4530

    🤖 This is an automated response from Repo Assist.

    I've submitted a draft PR that addresses this issue: I investigated the root cause and found that option_type() in fable-library-ts/Reflection.ts (and the equivalent in fable-library-py/fable_library/reflection.py) was building a TypeInfo without a cases property — the exact field that FSharpType.IsUnion and GetUnionCases rely on.

    The fix rewrites option_type() to expose None (tag 0) and Some (tag 1) as union cases, and adds option-aware handling in getUnionFields/makeUnion to account for Fable's erased option representation at runtime.

    Please check the PR for details and test coverage. CI will validate the build.

    Generated by Repo Assist · ● 10.1M · ◷

    To install this agentic workflow, run

    gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f
    
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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions