Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptTroubleshooting Types
This page shows the compiler errors that usually mean you crossed a wrapper boundary in the wrong place.
Most Axial type errors are not exotic. The compiler usually sees one wrapper shape and you intended another.
Error: A Flow Alias Does Not Match The Channels You Intended
Flow<'env, 'error, 'value> is the full workflow shape. The shorter aliases remove common channels:
| Alias | Expands to |
|---|---|
Flow<'value> |
Flow<unit, Never, 'value> |
Flow<'error, 'value> |
Flow<unit, 'error, 'value> |
EnvFlow<'env, 'value> |
Flow<'env, Never, 'value> |
ExnFlow<'value> |
Flow<unit, exn, 'value> |
ExnEnvFlow<'env, 'value> |
Flow<'env, exn, 'value> |
If your workflow reads an environment and has a typed domain error, use the full Flow<'env, 'error, 'value> form.
Error: A Unique Overload For Method Bind Could Not Be Determined
This usually happens when the compiler cannot tell which wrapper shape a let! value should use.
Example:
let nested : Async<Async<Result<int, string>>> =
async {
return async { return Ok 42 }
}
let workflow : Flow<unit, string, int> =
flow {
let! next = nested
let! value = next
return value
}
nested: Async<Async<Result<int,string>>>Microsoft.FSharp.Control.FSharpAsync`1An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception. This type has no members. Asynchronous computations are normally specified either by using an async expression or the static methods in the type. See also F# Language Guide - Async Workflows. Library functionality for asynchronous programming, events and agents. See also Asynchronous Programming, Events and Lazy Expressions in the F# Language Guide. Async Programming
Microsoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
intAn abbreviation for the CLI type . Basic Types
stringAn abbreviation for the CLI type . Basic Types
async: AsyncBuilderBuilds an asynchronous workflow using computation expression syntax. let sleepExample() = async { printfn "sleeping" do! Async.Sleep 10 printfn "waking up" return 6 } sleepExample() |> Async.RunSynchronously
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
workflow: Flow<unit,string,int>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
next: Async<Result<int,string>>value: intFix it with a type annotation:
let workflow : Flow<unit, string, int> =
flow {
let! next = nested
let! (value: int) = next
return value
}This usually means you wrote a smaller workflow against one env type (or a specific service contract) and are trying to run it inside a larger env.
Example 1: Records
type SmallEnv = { Prefix: string }
type BigEnv = { App: SmallEnv; RequestId: string }
let greet : Flow<SmallEnv, string, string> =
flow {
let! prefix = Flow.envWith _.Prefix
return $"{prefix} world"
}
// Run in BigEnv using localEnv
let greetInBigEnv : Flow<BigEnv, string, string> =
greet |> Flow.localEnv _.App
03-the-flow-type_07-troubleshooting-types.md_page.SmallEnvPrefix: stringstringAn abbreviation for the CLI type . Basic Types
03-the-flow-type_07-troubleshooting-types.md_page.BigEnvApp: SmallEnvRequestId: stringgreet: Flow<SmallEnv,string,string>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
flow: FlowBuilderThe universal flow { } computation expression.
prefix: stringAxial.FlowenvWith: ('env -> 'value) -> Flow<'env,'error,'value>Projects one value from the current environment. This is the primary way to access app dependencies, configuration, or request metadata stored in env. The projection runs only when the flow is executed, so constructing the flow is still pure and side-effect free. Prefer small projections over passing a large environment deeper into reusable helpers. A function that extracts a value from the environment. A containing the projected value. let currentTime () = Flow.envWith (fun (environment: BaseRuntime) -> environment.Clock.UtcNow())
_arg1: SmallEnvgreetInBigEnv: Flow<BigEnv,string,string>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
localEnv: ('outerEnvironment -> 'innerEnvironment) -> Flow<'innerEnvironment,'error,'value> -> Flow<'outerEnvironment,'error,'value>Runs a flow against an environment derived from the outer environment. Use this to embed a smaller workflow inside a larger application environment without changing the smaller workflow's type. The mapping is applied at execution time. This is useful for preserving narrow helper signatures while still running everything from one app boundary. A function that maps the outer environment to the inner environment. The flow to run with the inner environment. A flow that expects the outer environment. let flow = Flow.succeed 1 |> Flow.localEnv (fun outer -> outer)
_arg1: BigEnvIf a helper requires IHasDatabase but you are running it in an environment that doesn't implement it, the compiler will error.
let helper : Flow<#IHasDatabase, _, _> = ...
// This fails if AppEnv doesn't implement IHasDatabase
let run (env: AppEnv) = helper |> Flow.startTask envError: Option Or ValueOption Does Not Match Your Error Type
Implicit option binding only works when the workflow error type is unit.
This fails:
let workflow : Flow<unit, string, int> =
flow {
let! value = Some 42
return value
}let optionWorkflow : Flow<unit, string, int> =
Some 42
|> Flow.fromOption "missing value"
optionWorkflow: Flow<unit,string,int>Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
stringAn abbreviation for the CLI type . Basic Types
intAn abbreviation for the CLI type . Basic Types
SomeThe representation of "Value of type 'T" The input value. An option representing the value.
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.FlowfromOption: 'error -> 'value option -> Flow<'env,'error,'value>Lifts an option into a synchronous flow with the supplied error. The error to return if the option is None. The option to lift. A flow that succeeds with the option's value or fails with the provided error. let opt = Some "value" Flow.fromOption "missing" opt |> Flow.run ()
Raw Task<'value> and ValueTask<'value> values do not bind directly in flow { }. A task is already running, while
Flow is a cold description that may run more than once.
Wrap work that should start with the Flow in ColdTask:
let load : ColdTask<int> =
ColdTask(fun cancellationToken -> service.loadAsync cancellationToken)
let workflow =
flow {
let! value = load
return value
}When work has already started, name that lifecycle explicitly:
let workflow =
flow {
let! value = Flow.awaitStartedTask runningTask
return value
}When Type Errors Usually Mean A Boundary Problem
If the compiler error mentions one of these shapes, check the boundary first:
Result<...>Async<...>Async<Result<...>>Task<...>Task<Result<...>>Flow<...>
Retry and repeat live on Schedule (Schedule.retry, Schedule.repeat), not on Flow, to avoid ambiguity with shorter Flow aliases.
Most fixes are one of:
- add a type annotation to disambiguate
let!overloads - derive a smaller local environment with
localEnv - use
Bind.errororBind.mapErrorat aflow { }bind site when the source error must be assigned or mapped first - move back to plain Result until the real workflow boundary appears

