Exceptions & Debugging

Transpose supports the full C# exception model, and the emitted JavaScript is readable enough to debug directly in the browser.

Exception handling

try, catch, finally, exception filters (when), and rethrow all work as they do in .NET:

try
{
    ThrowException();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("retry"))
{
    Console.WriteLine("Filtered: " + ex.Message);
}
catch (InvalidOperationException ex)
{
    Console.WriteLine("Caught specific exception: " + ex.Message);
}
catch (Exception ex)
{
    Console.WriteLine("Caught general exception: " + ex.Message);
}
finally
{
    Console.WriteLine("Finally block executed");
}

public void ThrowException()
{
    throw new InvalidOperationException("Something went wrong!");
}

using / await using and IDisposable are supported, so deterministic cleanup works too.

Exceptions and JavaScript errors

Transpose emits one JavaScript catch per try and dispatches to the matching C# clause by type at runtime. A thrown value that is not already a System.Exception is wrapped, so a native error thrown by library code is still catchable as a .NET exception. The mapping is:

Thrown JavaScript value Caught as
A System.Exception (from C# code) itself
TypeError System.NullReferenceException
RangeError System.ArgumentOutOfRangeException
Any other Error System.SystemException
Anything else System.Exception with the value's message

The original error is kept on the wrapper, which is what StackTrace reads, so the JavaScript stack survives into the .NET-shaped exception. InnerException, Message and Data behave as expected.

In the other direction, an exception thrown from C# is a System.Exception instance, not a JavaScript Error. JavaScript code that catches it should read .message (which the runtime provides) rather than expect an Error prototype.

Debugging

Source maps are not emitted yet

Transpose does not currently produce .js.map files, so you cannot set breakpoints in C# source in the browser. This is a known gap. Debug against the emitted JavaScript instead — the notes below make that practical.

Build with formatted output

For a debugging session, make sure the readable bundle is what the page loads:

tps.Debug.json
{
  "outputFormatting": "Formatted"
}

A Debug build already wires the non-minified files into index.html, so plain dotnet build is usually enough — see Build Configurations.

Read the emitted JavaScript

The output keeps your names, which makes it far easier to navigate than typical compiler output:

  • A type appears as Transpose.define("MyNamespace.MyClass", { … }), so searching the bundle for the C# type name finds it.
  • Methods keep their C# names. Overloads are numbered with a $1, $2 … suffix (Foo, Foo$1), and constructors are $ctor1, $ctor2, ….
  • Auto-properties become plain fields; properties with bodies become get_Name / set_Name.
  • Locals keep their C# names where JavaScript allows it.

Set breakpoints on those functions in the browser's Sources panel. Because the output is reproducible, the line you break on stays put across rebuilds unless the code above it changed.

Watch mode keeps the loop short

tps --watch rebuilds and reloads the page on every save, so the edit-inspect-fix cycle does not involve a manual build. See Watch mode.

Script.Debugger

Script.Debugger() emits a JavaScript debugger; statement, which breaks into the devtools at that point when they are open:

using Transpose;

if (state == null)
{
    Script.Debugger();
}

Common issues

Symptom Likely cause
x is not a function on a value from a library The C# binding's [Name] or [Template] does not match the JavaScript API. Check the emitted call against the library's real surface.
setItem is not a function after assigning an array to a Span<T> The implicit array-to-Span<T> conversion is not modelled. Use the array directly — see How It Works.
A struct assignment writes through to the original Value-copy semantics apply only to structs declared in the project being compiled. BCL structs, library structs and ValueTuple copy by reference.
A reflection call finds nothing reflection.disabled is on, or the type carries [Reflectable(false)]. See Reflection.
The whole bundle fails to parse A syntax error in a Script.Write / [Script] / [Template] body. Raw JavaScript you supply is emitted verbatim and is not validated.
The page loads but nothing runs tps.js is not loaded before the bundle, or the entry point is in a type the compiler did not see. Check the script order in index.html.
© 2026 Curiosity. All rights reserved.