diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml
index a6b7d41d..4b85125e 100644
--- a/.github/workflows/update-changelog.yaml
+++ b/.github/workflows/update-changelog.yaml
@@ -33,7 +33,7 @@ jobs:
         run: perl -i -pe 'BEGIN{undef $/} s/(## (?:\[)?v[\d.]+(?:\](?:\([^)]+\))?)? - \d{4}-\d{2}-\d{2}\n)[\s\S]*?### Changes\n+/$1\n/g' CHANGELOG.md
 
       - name: Create Pull Request
-        id: cpr
+        id: cpr 
         uses: peter-evans/create-pull-request@v7
         with:
           branch: changelog/${{ github.event.release.tag_name }}
@@ -48,7 +48,6 @@ jobs:
           commit-message: "chore: update changelog for ${{ github.event.release.tag_name }}"
           labels: "automated,changelog,chore"
           delete-branch: true
-          sign-commits: true
 
       - name: Enable Auto-merge
         if: steps.cpr.outputs.pull-request-number
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76c82c0a..2ffe12b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,38 @@
 All notable changes to this project will be documented in this file.
 See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
 
-## [Unreleased](https://github.com/j-d-ha/aws-lambda-host/compare/v1.2.1...HEAD)
+## [Unreleased](https://github.com/j-d-ha/aws-lambda-host/compare/v1.3.1...HEAD)
+
+## [v1.3.1](https://github.com/j-d-ha/aws-lambda-host/compare/v1.3.0...v1.3.1) - 2025-12-10
+
+### 🐛 Bug Fixes
+
+* fix(core): add missing TenantId and TraceId properties to DefaultLambdaHostContext (#224) @j-d-ha
+* fix(ci): remove sign-commits from changelog workflow (#223) @j-d-ha
+
+## [v1.3.0](https://github.com/j-d-ha/aws-lambda-host/compare/v1.2.1...v1.3.0) - 2025-12-10
+
+## 🚀 Features
+
+* feat(source-generators): support multiple MapHandler invocations with custom feature providers (#214) @j-d-ha
+* docs: update MkDocs palette toggle configuration (#211) @j-d-ha
+
+## 🐛 Bug Fixes
+
+* fix: update third-party license attributions (#217) @j-d-ha
+
+## 📚 Documentation
+
+* docs: add comprehensive getting started guide and restructure documentation (#209) @j-d-ha
+
+## 🔄 Refactoring
+
+* refactor(host): migrate BootstrapHttpClient from options to dependency injection (#219) @j-d-ha
+* refactor(docs): replace ASPNETCORE_ENVIRONMENT with DOTNET_ENVIRONMENT (#216) @j-d-ha
+
+## 🔧 Maintenance
+
+* ci(github): optimize workflow triggers for draft PRs (#215) @j-d-ha
 
 ## [v1.2.1](https://github.com/j-d-ha/aws-lambda-host/compare/v1.2.0...v1.2.1) - 2025-11-30
 
diff --git a/examples/AwsLambda.Host.Example.Testing/Lambda/Program.cs b/examples/AwsLambda.Host.Example.Testing/Lambda/Program.cs
index fc3329b0..e641ddc1 100644
--- a/examples/AwsLambda.Host.Example.Testing/Lambda/Program.cs
+++ b/examples/AwsLambda.Host.Example.Testing/Lambda/Program.cs
@@ -1,4 +1,5 @@
 ﻿using AwsLambda.Host.Builder;
+using AwsLambda.Host.Core;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 
@@ -13,8 +14,30 @@
 // Build the Lambda application
 var lambda = builder.Build();
 
+// throw new Exception("Init failed");
+
+// lambda.OnInit(() =>
+// {
+//     // throw new Exception("Init failed");
+//     // return false;
+// });
+
 // Map your handler - the event is automatically injected
-lambda.MapHandler(([Event] string name) => $"Hello {name}!");
+lambda.MapHandler(
+    async ([Event] string name, ILambdaHostContext context, CancellationToken cancellationToken) =>
+    {
+        await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken);
+        if (string.IsNullOrWhiteSpace(name))
+            throw new ArgumentNullException(nameof(name), "Name is required.");
+
+        return $"Hello {name}!";
+    }
+);
+
+lambda.OnShutdown(() =>
+{
+    Console.WriteLine("Shutdown");
+});
 
 // Run the Lambda
 await lambda.RunAsync();
diff --git a/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs b/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs
index bcab7c64..81a1e338 100644
--- a/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs
+++ b/examples/AwsLambda.Host.Example.Testing/Tests/LambdaHostTest.cs
@@ -1,3 +1,4 @@
+using AwesomeAssertions;
 using AwsLambda.Host.Options;
 using AwsLambda.Host.Testing;
 using JetBrains.Annotations;
@@ -14,19 +15,63 @@ public async Task LambdaHost_CanStartWithoutError()
     {
         await using var factory = new LambdaApplicationFactory<Program>();
 
-        var client = factory.CreateClient();
-        // No need to wait for next request - server handles this automatically
-        var response = await client.InvokeAsync<string, string>(
+        var setup = await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
+
+        setup.InitStatus.Should().Be(InitStatus.InitCompleted);
+
+        var response = await factory.TestServer.InvokeAsync<string, string>(
             "Jonas",
+            "1",
             TestContext.Current.CancellationToken
         );
-        Assert.True(response.WasSuccess);
-        Assert.NotNull(response);
-        Assert.Equal("Hello Jonas!", response.Response);
+
+        response.WasSuccess.Should().BeTrue();
+        response.Should().NotBeNull();
+        response.Response.Should().Be("Hello Jonas!");
     }
 
     [Fact]
-    public async Task LambdaHost_CrashesWithBadConfiguration_ThrowsException()
+    public async Task LambdaHost_HandlerReturnsError()
+    {
+        await using var factory = new LambdaApplicationFactory<Program>();
+
+        var setup = await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
+
+        setup.InitStatus.Should().Be(InitStatus.InitCompleted);
+
+        var response = await factory.TestServer.InvokeAsync<string, string>(
+            "",
+            TestContext.Current.CancellationToken
+        );
+
+        response.WasSuccess.Should().BeFalse();
+        response.Error.Should().NotBeNull();
+        response.Error?.ErrorMessage.Should().Be("Name is required. (Parameter 'name')");
+    }
+
+    [Fact]
+    public async Task LambdaHost_CanBeShutdown()
+    {
+        await using var factory = new LambdaApplicationFactory<Program>();
+
+        var setup = await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
+
+        setup.InitStatus.Should().Be(InitStatus.InitCompleted);
+
+        var response = await factory.TestServer.InvokeAsync<string, string>(
+            "Jonas",
+            TestContext.Current.CancellationToken
+        );
+
+        response.WasSuccess.Should().BeTrue();
+        response.Should().NotBeNull();
+        response.Response.Should().Be("Hello Jonas!");
+
+        await factory.TestServer.StopAsync(TestContext.Current.CancellationToken);
+    }
+
+    [Fact]
+    public async Task LambdaHost_ServerInternalExceptions_AreCaughtAndReturnedAsError()
     {
         await using var factory = new LambdaApplicationFactory<Program>().WithHostBuilder(builder =>
         {
@@ -41,28 +86,30 @@ public async Task LambdaHost_CrashesWithBadConfiguration_ThrowsException()
             );
         });
 
-        var client = factory.CreateClient();
-        // No need to wait for next request - server handles this automatically
-        var response = await client.InvokeAsync<string, string>(
-            "Jonas",
-            TestContext.Current.CancellationToken
-        );
-        Assert.True(response.WasSuccess);
-        Assert.NotNull(response);
-        Assert.Equal("Hello Jonas!", response.Response);
+        var act = async () =>
+            await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
+
+        (await act.Should().ThrowAsync<AggregateException>())
+            .And.InnerExceptions.Should()
+            .ContainSingle(ex =>
+                ex is InvalidOperationException
+                && ex.Message.Contains(
+                    "Unexpected request received from the Lambda HTTP handler: GET http://http//localhost:3002/2018-06-01/runtime/invocation/next"
+                )
+            );
     }
 
     [Fact]
     public async Task LambdaHost_ProcessesConcurrentInvocationsInFifoOrder()
     {
         await using var factory = new LambdaApplicationFactory<Program>();
-        var client = factory.CreateClient();
+        await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
 
         // Launch 5 concurrent invocations
         var tasks = Enumerable
             .Range(1, 5)
             .Select(i =>
-                client.InvokeAsync<string, string>(
+                factory.TestServer.InvokeAsync<string, string>(
                     $"User{i}",
                     TestContext.Current.CancellationToken
                 )
@@ -84,9 +131,9 @@ public async Task LambdaHost_ProcessesConcurrentInvocationsInFifoOrder()
     public async Task InvokeAsync_WithInvalidPayload_ReturnsError()
     {
         await using var factory = new LambdaApplicationFactory<Program>();
-        var client = factory.CreateClient();
+        await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
 
-        var response = await client.InvokeAsync<string, int>(
+        var response = await factory.TestServer.InvokeAsync<string, int>(
             123,
             TestContext.Current.CancellationToken
         );
@@ -100,38 +147,46 @@ public async Task InvokeAsync_WithInvalidPayload_ReturnsError()
     public async Task InvokeAsync_WithPreCanceledToken_CancelsInvocation()
     {
         await using var factory = new LambdaApplicationFactory<Program>();
-        var client = factory.CreateClient();
+        await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
 
         using var cts = new CancellationTokenSource();
         await cts.CancelAsync();
 
         await Assert.ThrowsAsync<TaskCanceledException>(() =>
-            client.InvokeAsync<string, string>("Jonas", cts.Token)
+            factory.TestServer.InvokeAsync<string, string>("Jonas", cts.Token)
         );
     }
 
-    [Fact]
-    public async Task InvokeAsync_WithZeroTimeout_CancelsInvocation() =>
-        await Assert.ThrowsAsync<AggregateException>(async () =>
-        {
-            try
-            {
-                await using var factory = new LambdaApplicationFactory<Program>();
-                var client = factory
-                    .CreateClient()
-                    .ConfigureOptions(options =>
-                        options.InvocationHeaderOptions.ClientWaitTimeout = TimeSpan.Zero
-                    );
-
-                await client.InvokeAsync<string, string>(
-                    "Jonas",
-                    TestContext.Current.CancellationToken
-                );
-            }
-            catch (Exception e)
-            {
-                Console.WriteLine(e.GetType().FullName);
-                throw;
-            }
-        });
+    // [Fact]
+    // public async Task InvokeAsync_WithZeroTimeout_CancelsInvocation() =>
+    //     await Assert.ThrowsAsync<TaskCanceledException>(async () =>
+    //     {
+    //         await using var factory = new LambdaApplicationFactory<Program>();
+    //         await factory.TestServer.StartAsync(TestContext.Current.CancellationToken);
+    //
+    //         var options = new LambdaServerOptions();
+    //         options.InvocationOptions.ClientWaitTimeout = TimeSpan.Zero;
+    //
+    //         await factory.TestServer.InvokeAsync<string, string>(
+    //             "Jonas",
+    //             options,
+    //             TestContext.Current.CancellationToken
+    //         );
+    //     });
+
+    // [Fact]
+    // public async Task StartAsync_WithFailingInit_ReturnsInitError()
+    // {
+    //     // This test verifies that when OnInit returns false (as configured in Program.cs),
+    //     // the runtime posts to /runtime/init/error and StartAsync returns InitResponse with
+    //     //error
+    //     await using var factory = new LambdaApplicationFactory<Program>();
+    //
+    //     using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+    //     var initResponse = await factory.TestServer.StartAsync(cts.Token);
+    //
+    //     Assert.False(initResponse.InitSuccess);
+    //     Assert.NotNull(initResponse.Error);
+    //     Assert.Equal(ServerState.Stopped, factory.TestServer.State);
+    // }
 }
diff --git a/src/AwsLambda.Host.Abstractions/Options/DefaultLambdaJsonSerializerOptions.cs b/src/AwsLambda.Host.Abstractions/Options/DefaultLambdaJsonSerializerOptions.cs
new file mode 100644
index 00000000..c81de4a4
--- /dev/null
+++ b/src/AwsLambda.Host.Abstractions/Options/DefaultLambdaJsonSerializerOptions.cs
@@ -0,0 +1,35 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Amazon.Lambda.Serialization.SystemTextJson;
+using Amazon.Lambda.Serialization.SystemTextJson.Converters;
+
+namespace AwsLambda.Host.Options;
+
+/// <summary>Provides the default JSON serializer options used by AWS Lambda.</summary>
+public static class DefaultLambdaJsonSerializerOptions
+{
+    /// <summary>
+    ///     Creates a <see cref="JsonSerializerOptions" /> instance that matches the defaults used by
+    ///     <see cref="DefaultLambdaJsonSerializer" />.
+    /// </summary>
+    /// <remarks>
+    ///     <para>Configures null-value ignoring, case-insensitive property names, and the AWS naming policy.</para>
+    ///     <para>Adds the AWS-provided converters for dates, memory streams, constant classes, and byte arrays.</para>
+    /// </remarks>
+    /// <returns>Configured JSON serializer options suitable for AWS Lambda payloads.</returns>
+    public static JsonSerializerOptions Create()
+    {
+        var options = new JsonSerializerOptions
+        {
+            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+            PropertyNameCaseInsensitive = true,
+            PropertyNamingPolicy = new AwsNamingPolicy(),
+        };
+        options.Converters.Add(new DateTimeConverter());
+        options.Converters.Add(new MemoryStreamConverter());
+        options.Converters.Add(new ConstantClassConverter());
+        options.Converters.Add(new ByteArrayConverter());
+
+        return options;
+    }
+}
diff --git a/src/AwsLambda.Host.Abstractions/Options/EnvelopeOptions.cs b/src/AwsLambda.Host.Abstractions/Options/EnvelopeOptions.cs
index 8dc0cc2e..56799568 100644
--- a/src/AwsLambda.Host.Abstractions/Options/EnvelopeOptions.cs
+++ b/src/AwsLambda.Host.Abstractions/Options/EnvelopeOptions.cs
@@ -1,8 +1,6 @@
 using System.Text.Json;
-using System.Text.Json.Serialization;
 using System.Xml;
 using Amazon.Lambda.Serialization.SystemTextJson;
-using Amazon.Lambda.Serialization.SystemTextJson.Converters;
 using AwsLambda.Host.Envelopes;
 
 namespace AwsLambda.Host.Options;
@@ -57,28 +55,8 @@ public class EnvelopeOptions
     ///         not been explicitly configured.
     ///     </para>
     /// </remarks>
-    public JsonSerializerOptions LambdaDefaultJsonOptions
-    {
-        get
-        {
-            if (field is null)
-            {
-                field = new JsonSerializerOptions
-                {
-                    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
-                    PropertyNameCaseInsensitive = true,
-                    PropertyNamingPolicy = new AwsNamingPolicy(),
-                };
-                field.Converters.Add(new DateTimeConverter());
-                field.Converters.Add(new MemoryStreamConverter());
-                field.Converters.Add(new ConstantClassConverter());
-                field.Converters.Add(new ByteArrayConverter());
-            }
-
-            return field;
-        }
-        set;
-    }
+    public JsonSerializerOptions LambdaDefaultJsonOptions { get; set; } =
+        DefaultLambdaJsonSerializerOptions.Create();
 
     /// <summary>Gets or sets the XML reader settings used when deserializing Lambda event payloads.</summary>
     /// <remarks>
diff --git a/src/AwsLambda.Host.Testing/DeferredHostBuilder.cs b/src/AwsLambda.Host.Testing/DeferredHostBuilder.cs
index 7fe11895..6e3d1fe0 100644
--- a/src/AwsLambda.Host.Testing/DeferredHostBuilder.cs
+++ b/src/AwsLambda.Host.Testing/DeferredHostBuilder.cs
@@ -25,6 +25,10 @@ internal sealed class DeferredHostBuilder : IHostBuilder
         TaskCreationOptions.RunContinuationsAsynchronously
     );
 
+    private readonly TaskCompletionSource<Exception?> _entryPointCompletionTcs = new(
+        TaskCreationOptions.RunContinuationsAsynchronously
+    );
+
     private Action<IHostBuilder> _configure;
     private Func<string[], object>? _hostFactory;
 
@@ -39,6 +43,8 @@ public DeferredHostBuilder() =>
 
     public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
 
+    public Task<Exception?> EntryPointCompletion => _entryPointCompletionTcs.Task;
+
     public IHost Build()
     {
         // Hosting configuration is being provided by args so that
@@ -116,10 +122,12 @@ public void EntryPointCompleted(Exception? exception)
         if (exception is not null)
         {
             _hostStartTcs.TrySetException(exception);
+            _entryPointCompletionTcs.TrySetResult(exception);
         }
         else
         {
             _hostStartTcs.TrySetResult();
+            _entryPointCompletionTcs.TrySetResult(null);
         }
     }
 
diff --git a/src/AwsLambda.Host.Testing/DictionaryExtensions.cs b/src/AwsLambda.Host.Testing/DictionaryExtensions.cs
new file mode 100644
index 00000000..7a8e5afc
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/DictionaryExtensions.cs
@@ -0,0 +1,19 @@
+namespace AwsLambda.Host.Testing;
+
+internal static class DictionaryExtensions
+{
+    extension<TKey, TValue>(IDictionary<TKey, TValue> dictionary)
+    {
+        internal void AddRequired(TKey key, TValue value)
+        {
+            if (!dictionary.TryAdd(key, value))
+                throw new InvalidOperationException($"Key '{key}' already exists.");
+        }
+
+        internal void GetRequired(TKey? key, out TValue value)
+        {
+            if (key is null || !dictionary.TryGetValue(key, out value!))
+                throw new InvalidOperationException($"Key '{key}' is null or does not exist.");
+        }
+    }
+}
diff --git a/src/AwsLambda.Host.Testing/ILambdaRuntimeRouteManager.cs b/src/AwsLambda.Host.Testing/ILambdaRuntimeRouteManager.cs
deleted file mode 100644
index 041586b6..00000000
--- a/src/AwsLambda.Host.Testing/ILambdaRuntimeRouteManager.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.AspNetCore.Routing;
-
-namespace AwsLambda.Host.Testing;
-
-internal interface ILambdaRuntimeRouteManager
-{
-    bool TryMatch(
-        HttpRequestMessage request,
-        out RequestType? routeType,
-        out RouteValueDictionary? values
-    );
-}
diff --git a/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs b/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs
index 440371dc..a04f3925 100644
--- a/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs
+++ b/src/AwsLambda.Host.Testing/LambdaApplicationFactory.cs
@@ -73,7 +73,7 @@ public class LambdaApplicationFactory<TEntryPoint> : IDisposable, IAsyncDisposab
     /// <summary>
     /// Gets the <see cref="LambdaTestServer"/> created by this <see cref="LambdaApplicationFactory{TEntryPoint}"/>.
     /// </summary>
-    internal LambdaTestServer Server
+    public LambdaTestServer TestServer
     {
         get
         {
@@ -85,14 +85,7 @@ internal LambdaTestServer Server
     /// <summary>
     /// Gets the <see cref="IServiceProvider"/> created by the server associated with this <see cref="LambdaApplicationFactory{TEntryPoint}"/>.
     /// </summary>
-    public virtual IServiceProvider Services
-    {
-        get
-        {
-            EnsureServer();
-            return _host!.Services;
-        }
-    }
+    public virtual IServiceProvider Services => TestServer.Services;
 
     /// <inheritdoc />
     public virtual async ValueTask DisposeAsync()
@@ -106,15 +99,10 @@ public virtual async ValueTask DisposeAsync()
         foreach (var factory in _derivedFactories)
             await ((IAsyncDisposable)factory).DisposeAsync().ConfigureAwait(false);
 
+        // TestServer handles disposing both processor and host
         if (_server != null)
             await _server.DisposeAsync().ConfigureAwait(false);
 
-        if (_host != null)
-        {
-            await _host.StopAsync().ConfigureAwait(false);
-            _host?.Dispose();
-        }
-
         _disposedAsync = true;
 
         Dispose(true);
@@ -134,12 +122,6 @@ public void Dispose()
     /// </summary>
     ~LambdaApplicationFactory() => Dispose(false);
 
-    public LambdaClient CreateClient()
-    {
-        EnsureServer();
-        return _server!.CreateLambdaClient();
-    }
-
     /// <summary>
     /// Creates a new <see cref="LambdaApplicationFactory{TEntryPoint}"/> with a <see cref="IHostBuilder"/>
     /// that is further customized by <paramref name="configuration"/>.
@@ -152,15 +134,12 @@ public LambdaApplicationFactory<TEntryPoint> WithHostBuilder(
         Action<IHostBuilder> configuration
     ) => WithHostBuilderCore(configuration);
 
-    internal virtual LambdaTestServer CreateServer() => new();
-
     internal virtual LambdaApplicationFactory<TEntryPoint> WithHostBuilderCore(
         Action<IHostBuilder> configuration
     )
     {
         var factory = new DelegatedLambdaApplicationFactory(
             ClientOptions,
-            CreateServer,
             CreateHost,
             CreateHostBuilder,
             GetTestAssemblies,
@@ -218,7 +197,7 @@ private void EnsureServer()
             // to IHostBuilder.Build.
             deferredHostBuilder.SetHostFactory(factory);
 
-            ConfigureHostBuilder(deferredHostBuilder);
+            ConfigureHostBuilder(deferredHostBuilder, deferredHostBuilder.EntryPointCompletion);
             return;
         }
 
@@ -226,17 +205,20 @@ private void EnsureServer()
     }
 
     [MemberNotNull(nameof(_server))]
-    private void ConfigureHostBuilder(IHostBuilder hostBuilder)
+    private void ConfigureHostBuilder(
+        IHostBuilder hostBuilder,
+        Task<Exception?> entryPointCompletion
+    )
     {
         SetContentRoot(hostBuilder);
         _configuration(hostBuilder);
 
-        _server = CreateServer();
+        _server = new LambdaTestServer(entryPointCompletion);
 
         // set Lambda Bootstrap Http Client
         hostBuilder.ConfigureServices(services =>
         {
-            services.AddLambdaBootstrapHttpClient(new HttpClient(_server.CreateTestingHandler()));
+            services.AddLambdaBootstrapHttpClient(new HttpClient(_server.CreateHandler()));
 
             services.PostConfigure<LambdaHostOptions>(options =>
             {
@@ -245,10 +227,13 @@ private void ConfigureHostBuilder(IHostBuilder hostBuilder)
             });
         });
 
+        // Build the host but DON'T start it - server will start it
         _host = CreateHost(hostBuilder);
+        _server.SetHost(_host);
 
-        // Start the server's background processing loop after host is ready
-        _server.Start();
+        // Create the public server with the built (but not started) host
+        // _server = new LambdaTestServer(_host, processor, entryPointCompletion:
+        // entryPointCompletion);
     }
 
     private void SetContentRoot(IHostBuilder builder)
@@ -440,12 +425,9 @@ private static void EnsureDepsFile()
     /// </summary>
     /// <param name="builder">The <see cref="IHostBuilder"/> used to create the host.</param>
     /// <returns>The <see cref="IHost"/> with the bootstrapped application.</returns>
-    protected virtual IHost CreateHost(IHostBuilder builder)
-    {
-        var host = builder.Build();
-        host.Start();
-        return host;
-    }
+    protected virtual IHost CreateHost(IHostBuilder builder) =>
+        // Build the host but DON'T start it - LambdaTestServer.StartAsync() will start it
+        builder.Build();
 
     /// <summary>
     /// Gives a fixture an opportunity to configure the application before it gets built.
@@ -475,12 +457,10 @@ private sealed class DelegatedLambdaApplicationFactory : LambdaApplicationFactor
     {
         private readonly Func<IHostBuilder, IHost> _createHost;
         private readonly Func<IHostBuilder?> _createHostBuilder;
-        private readonly Func<LambdaTestServer> _createServer;
         private readonly Func<IEnumerable<Assembly>> _getTestAssemblies;
 
         public DelegatedLambdaApplicationFactory(
             LambdaApplicationFactoryClientOptions options,
-            Func<LambdaTestServer> createServer,
             Func<IHostBuilder, IHost> createHost,
             Func<IHostBuilder?> createHostBuilder,
             Func<IEnumerable<Assembly>> getTestAssemblies,
@@ -488,7 +468,6 @@ Action<IHostBuilder> configureWebHost
         )
         {
             ClientOptions = options;
-            _createServer = createServer;
             _createHost = createHost;
             _createHostBuilder = createHostBuilder;
             _getTestAssemblies = getTestAssemblies;
@@ -497,8 +476,6 @@ Action<IHostBuilder> configureWebHost
 
         protected override IHost CreateHost(IHostBuilder builder) => _createHost(builder);
 
-        internal override LambdaTestServer CreateServer() => _createServer();
-
         protected override IHostBuilder? CreateHostBuilder() => _createHostBuilder();
 
         protected override IEnumerable<Assembly> GetTestAssemblies() => _getTestAssemblies();
@@ -510,7 +487,6 @@ Action<IHostBuilder> configuration
         ) =>
             new DelegatedLambdaApplicationFactory(
                 ClientOptions,
-                _createServer,
                 _createHost,
                 _createHostBuilder,
                 _getTestAssemblies,
diff --git a/src/AwsLambda.Host.Testing/LambdaHttpHandler.cs b/src/AwsLambda.Host.Testing/LambdaHttpHandler.cs
index bf7cc1a9..ffc9f4df 100644
--- a/src/AwsLambda.Host.Testing/LambdaHttpHandler.cs
+++ b/src/AwsLambda.Host.Testing/LambdaHttpHandler.cs
@@ -41,7 +41,7 @@ CancellationToken cancellationToken
         }
         catch (ChannelClosedException)
         {
-            // Server is shutting down; propagate cancellation to caller
+            // TestServer is shutting down; propagate cancellation to caller
             var canceled = new TaskCompletionSource<HttpResponseMessage>(
                 TaskCreationOptions.RunContinuationsAsynchronously
             );
diff --git a/src/AwsLambda.Host.Testing/LambdaRuntimeRouteManager.cs b/src/AwsLambda.Host.Testing/LambdaRuntimeRouteManager.cs
index e60d02c3..0f2fd484 100644
--- a/src/AwsLambda.Host.Testing/LambdaRuntimeRouteManager.cs
+++ b/src/AwsLambda.Host.Testing/LambdaRuntimeRouteManager.cs
@@ -4,7 +4,7 @@
 
 namespace AwsLambda.Host.Testing;
 
-internal class LambdaRuntimeRouteManager : ILambdaRuntimeRouteManager
+internal class LambdaRuntimeRouteManager
 {
     private static readonly RouteTemplate[] Routes =
     [
@@ -18,6 +18,15 @@ internal class LambdaRuntimeRouteManager : ILambdaRuntimeRouteManager
             ),
         },
         new()
+        {
+            Type = RequestType.PostInitError,
+            Method = HttpMethod.Post.Method,
+            Matcher = new TemplateMatcher(
+                TemplateParser.Parse("{version}/runtime/init/error"),
+                new RouteValueDictionary()
+            ),
+        },
+        new()
         {
             Type = RequestType.PostResponse,
             Method = HttpMethod.Post.Method,
diff --git a/src/AwsLambda.Host.Testing/LambdaTestClient.cs b/src/AwsLambda.Host.Testing/LambdaTestClient.cs
deleted file mode 100644
index cb370a24..00000000
--- a/src/AwsLambda.Host.Testing/LambdaTestClient.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-using System.Net;
-using System.Net.Http.Json;
-using System.Text;
-using System.Text.Json;
-
-namespace AwsLambda.Host.Testing;
-
-/// <summary>
-/// Client for invoking Lambda functions in tests.
-/// Provides a clean API that abstracts HTTP details.
-/// </summary>
-public class LambdaClient
-{
-    private readonly JsonSerializerOptions _jsonSerializerOptions;
-    private readonly LambdaClientOptions _lambdaClientOptions;
-    private readonly LambdaTestServer _server;
-    private int _requestCounter;
-
-    internal LambdaClient(LambdaTestServer server, JsonSerializerOptions jsonSerializerOptions)
-    {
-        _server = server;
-        _jsonSerializerOptions = jsonSerializerOptions;
-        _lambdaClientOptions = new LambdaClientOptions();
-    }
-
-    /// <summary>
-    /// Configures client options for invocation headers.
-    /// </summary>
-    public LambdaClient ConfigureOptions(Action<LambdaClientOptions> configureOptions)
-    {
-        ArgumentNullException.ThrowIfNull(configureOptions);
-
-        configureOptions(_lambdaClientOptions);
-
-        return this;
-    }
-
-    /// <summary>
-    /// Invokes the Lambda function with the given event and waits for the response.
-    /// </summary>
-    public async Task<InvocationResponse<TResponse>> InvokeAsync<TResponse, TEvent>(
-        TEvent invokeEvent,
-        CancellationToken cancellationToken = default
-    )
-    {
-        // Generate unique request ID
-        var requestId = GetRequestId();
-
-        // Create the event response with Lambda headers
-        var eventResponse = CreateEventResponse(invokeEvent, requestId);
-        var deadlineUtc = DateTimeOffset.UtcNow.Add(
-            _lambdaClientOptions.InvocationHeaderOptions.ClientWaitTimeout
-        );
-
-        // Queue invocation and wait for Bootstrap to process it
-        var waitTimeout = _lambdaClientOptions.InvocationHeaderOptions.ClientWaitTimeout;
-
-        using var timeoutCts = new CancellationTokenSource(waitTimeout);
-        using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
-            cancellationToken,
-            timeoutCts.Token
-        );
-
-        var completion = await _server.QueueInvocationAsync(
-            requestId,
-            eventResponse,
-            deadlineUtc,
-            linkedCts.Token
-        );
-
-        var responseMessage = completion.Request;
-        var wasSuccess = completion.RequestType == RequestType.PostResponse;
-
-        var invocationResponse = new InvocationResponse<TResponse>
-        {
-            WasSuccess = wasSuccess,
-            Response = wasSuccess
-                ? await (
-                    responseMessage.Content?.ReadFromJsonAsync<TResponse>(
-                        _jsonSerializerOptions,
-                        cancellationToken
-                    ) ?? Task.FromResult<TResponse?>(default)
-                )
-                : default,
-            Error = !wasSuccess
-                ? await (
-                    responseMessage.Content?.ReadFromJsonAsync<ErrorResponse>(
-                        _jsonSerializerOptions,
-                        cancellationToken
-                    ) ?? Task.FromResult<ErrorResponse?>(null)
-                )
-                : null,
-        };
-
-        return invocationResponse;
-    }
-
-    private HttpResponseMessage CreateEventResponse<TEvent>(TEvent invokeEvent, string requestId)
-    {
-        var response = new HttpResponseMessage(HttpStatusCode.OK)
-        {
-            Content = new StringContent(
-                JsonSerializer.Serialize(invokeEvent, _jsonSerializerOptions),
-                Encoding.UTF8,
-                "application/json"
-            ),
-            Version = Version.Parse("1.1"),
-        };
-
-        // Add standard HTTP headers
-        response.Headers.Date = new DateTimeOffset(
-            _lambdaClientOptions.InvocationHeaderOptions.Date
-        );
-        response.Headers.TransferEncodingChunked = _lambdaClientOptions
-            .InvocationHeaderOptions
-            .TransferEncodingChunked;
-
-        // Add custom Lambda runtime headers
-        var deadlineMs = DateTimeOffset
-            .UtcNow.Add(_lambdaClientOptions.InvocationHeaderOptions.FunctionTimeout)
-            .ToUnixTimeMilliseconds();
-        response.Headers.Add("Lambda-Runtime-Deadline-Ms", deadlineMs.ToString());
-        response.Headers.Add("Lambda-Runtime-Aws-Request-Id", requestId);
-        response.Headers.Add(
-            "Lambda-Runtime-Trace-Id",
-            _lambdaClientOptions.InvocationHeaderOptions.TraceId
-        );
-        response.Headers.Add(
-            "Lambda-Runtime-Invoked-Function-Arn",
-            _lambdaClientOptions.InvocationHeaderOptions.FunctionArn
-        );
-
-        // Add any additional custom headers
-        foreach (var header in _lambdaClientOptions.InvocationHeaderOptions.AdditionalHeaders)
-            response.Headers.Add(header.Key, header.Value);
-
-        return response;
-    }
-
-    private string GetRequestId() =>
-        Interlocked.Increment(ref _requestCounter).ToString().PadLeft(12, '0');
-}
diff --git a/src/AwsLambda.Host.Testing/LambdaTestServer.cs b/src/AwsLambda.Host.Testing/LambdaTestServer.cs
index 773dceb3..357598e8 100644
--- a/src/AwsLambda.Host.Testing/LambdaTestServer.cs
+++ b/src/AwsLambda.Host.Testing/LambdaTestServer.cs
@@ -1,391 +1,420 @@
 using System.Collections.Concurrent;
 using System.Net;
+using System.Net.Http.Json;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Channels;
+using AwsLambda.Host.Options;
 using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
 
 namespace AwsLambda.Host.Testing;
 
-/// <summary>
-/// Test server that processes HTTP transactions from Lambda Bootstrap.
-/// Routes requests, queues invocations, and manages request-response correlation.
-/// </summary>
-internal class LambdaTestServer : IAsyncDisposable
+public class LambdaTestServer : IAsyncDisposable
 {
-    private const int TransactionChannelCapacity = 1024;
-    private const int NextRequestChannelCapacity = 1024;
-    private static readonly OperationCanceledException DisposedException = new(
-        "LambdaTestServer disposed"
-    );
-
-    private readonly LambdaClient _client;
-    private readonly JsonSerializerOptions _jsonSerializerOptions;
-    private readonly ConcurrentQueue<string> _pendingInvocationIds;
-    private readonly ConcurrentDictionary<string, PendingInvocation> _pendingInvocations;
-    private readonly Channel<LambdaHttpTransaction> _queuedNextRequests;
-    private readonly ILambdaRuntimeRouteManager _routeManager;
-    private readonly CancellationTokenSource _shutdownCts;
-    private readonly Channel<LambdaHttpTransaction> _transactionChannel;
-    private Task? _processingTask;
-
-    internal LambdaTestServer(
-        JsonSerializerOptions? jsonSerializerOptions = null,
-        ILambdaRuntimeRouteManager? routeManager = null
-    )
-    {
-        _transactionChannel = Channel.CreateBounded<LambdaHttpTransaction>(
-            new BoundedChannelOptions(TransactionChannelCapacity)
-            {
-                SingleReader = true,
-                SingleWriter = false,
-                FullMode = BoundedChannelFullMode.Wait,
-            }
-        );
-        _pendingInvocationIds = new ConcurrentQueue<string>();
-        _pendingInvocations = new ConcurrentDictionary<string, PendingInvocation>();
-        _queuedNextRequests = Channel.CreateBounded<LambdaHttpTransaction>(
-            new BoundedChannelOptions(NextRequestChannelCapacity)
-            {
-                SingleReader = false,
-                SingleWriter = false,
-                FullMode = BoundedChannelFullMode.Wait,
-            }
-        );
-        _routeManager = routeManager ?? new LambdaRuntimeRouteManager();
-        _jsonSerializerOptions = jsonSerializerOptions ?? new JsonSerializerOptions();
-        _shutdownCts = new CancellationTokenSource();
+    /// <summary>
+    /// Options used to configure how the server interacts with the Lambda.
+    /// </summary>
+    private readonly LambdaServerOptions _serverOptions;
 
-        // Create client that communicates with this server
-        _client = new LambdaClient(this, _jsonSerializerOptions);
-    }
+    /// <summary>
+    /// Task that represents the running Host application that has been captured.
+    /// </summary>
+    private readonly Task<Exception?> _entryPointCompletion;
 
-    public async ValueTask DisposeAsync()
-    {
-        await _shutdownCts.CancelAsync();
+    /// <summary>
+    /// TCS used to signal the startup has completed
+    /// </summary>
+    private readonly TaskCompletionSource<InitResponse> _initCompletionTcs = new(
+        TaskCreationOptions.RunContinuationsAsynchronously
+    );
 
-        _transactionChannel.Writer.TryComplete();
-        _queuedNextRequests.Writer.TryComplete();
+    /// <summary>
+    /// JSON serializer options used to serialize/deserilize Lambda events and responses.
+    /// </summary>
+    private readonly JsonSerializerOptions _jsonSerializerOptions;
 
-        // Cancel any transactions waiting for work
-        while (_queuedNextRequests.Reader.TryRead(out var queuedTransaction))
-            queuedTransaction.Fail(DisposedException);
+    /// <summary>
+    /// Channel used to queue pending invocations in a FIFO manner.
+    /// </summary>
+    private readonly Channel<string> _pendingInvocationIds = Channel.CreateUnbounded<string>(
+        new UnboundedChannelOptions { SingleReader = true, SingleWriter = false }
+    );
 
-        // Fail any in-flight transactions that haven't been processed yet
-        while (_transactionChannel.Reader.TryRead(out var transaction))
-            transaction.Fail(DisposedException);
+    /// <summary>
+    /// Dictionary to track all invocations that have been sent to Lambda.
+    /// </summary>
+    private readonly ConcurrentDictionary<string, PendingInvocation> _pendingInvocations = new();
 
-        // Cancel any pending invocations waiting for bootstrap responses
-        foreach (var pendingInvocation in _pendingInvocations.Values)
-            pendingInvocation.ResponseTcs.TrySetCanceled(_shutdownCts.Token);
+    /// <summary>
+    /// Route manager to determine the route of the incoming request from the Lambda.
+    /// </summary>
+    private readonly LambdaRuntimeRouteManager _routeManager = new();
 
-        if (_processingTask != null)
-        {
-            try
-            {
-                await _processingTask.ConfigureAwait(false);
-            }
-            catch (OperationCanceledException)
-            {
-                // Expected when task is canceled
-            }
-        }
+    /// <summary>
+    /// CTS used to signal shutdown of the server and cancellation of pending tasks.
+    /// </summary>
+    private readonly CancellationTokenSource _shutdownCts;
 
-        _shutdownCts.Dispose();
-    }
+    /// <summary>
+    /// Channel used to by the Lambda to send events to the server.
+    /// </summary>
+    private readonly Channel<LambdaHttpTransaction> _transactionChannel =
+        Channel.CreateUnbounded<LambdaHttpTransaction>(
+            new UnboundedChannelOptions { SingleReader = true, SingleWriter = false }
+        );
 
     /// <summary>
-    /// Creates the HTTP handler for Lambda Bootstrap to use.
+    /// Host application lifetime used to signal shutdown to the captioned Host.
     /// </summary>
-    internal HttpMessageHandler CreateTestingHandler() =>
-        new LambdaTestingHttpHandler(_transactionChannel);
+    private IHostApplicationLifetime _applicationLifetime;
 
     /// <summary>
-    /// Gets the client for test code to invoke Lambda functions.
+    /// The captured Host instance.
     /// </summary>
-    internal LambdaClient CreateLambdaClient() => _client;
+    private IHost? _host;
 
     /// <summary>
-    /// Starts the background processing loop.
-    /// Called automatically by LambdaApplicationFactory after host starts.
+    /// Task that is running the background processing loop to handle incoming requests from Lambda.
     /// </summary>
-    internal void Start()
-    {
-        if (_processingTask != null)
-            throw new InvalidOperationException("Server already started");
+    private Task? _processingTask;
 
-        _processingTask = Task.Run(ProcessTransactionsAsync);
-    }
+    /// <summary>
+    /// Counter used to generate unique request IDs.
+    /// </summary>
+    private int _requestCounter;
 
     /// <summary>
-    /// Queues a new invocation to be processed by Lambda Bootstrap.
-    /// Called by LambdaClient.InvokeAsync().
+    /// Current state of the server used to enforce lifecycle rules.
     /// </summary>
-    internal async Task<InvocationCompletion> QueueInvocationAsync(
-        string requestId,
-        HttpResponseMessage eventResponse,
-        DateTimeOffset deadlineUtc,
-        CancellationToken cancellationToken
+    private ServerState _state;
+
+    internal LambdaTestServer(
+        Task<Exception?>? entryPointCompletion,
+        CancellationToken shutdownToken = default
     )
     {
-        var pending = PendingInvocation.Create(requestId, eventResponse, deadlineUtc);
+        ArgumentNullException.ThrowIfNull(entryPointCompletion);
 
-        if (!_pendingInvocations.TryAdd(requestId, pending))
-            throw new InvalidOperationException($"Duplicate request ID: {requestId}");
+        _entryPointCompletion = entryPointCompletion;
+        _shutdownCts = CancellationTokenSource.CreateLinkedTokenSource(shutdownToken);
+        _state = ServerState.Created;
 
-        _pendingInvocationIds.Enqueue(requestId);
+        _jsonSerializerOptions = DefaultLambdaJsonSerializerOptions.Create();
+        _serverOptions = new LambdaServerOptions();
+    }
 
-        // If there's a queued /next request, serve it immediately
-        if (_queuedNextRequests.Reader.TryRead(out var nextTransaction))
-            RespondToNextRequest(nextTransaction);
+    public IServiceProvider Services => _host!.Services;
 
-        using var cancellationRegistration = cancellationToken.Register(() =>
-            CancelPendingInvocation(requestId, cancellationToken)
-        );
+    public async ValueTask DisposeAsync()
+    {
+        if (_state == ServerState.Running)
+            await StopAsync();
+
+        _transactionChannel.Writer.TryComplete();
 
-        // Wait for Bootstrap to process and respond
-        return await pending.ResponseTcs.Task.WaitAsync(cancellationToken);
+        await _shutdownCts.CancelAsync();
+
+        _state = ServerState.Disposed;
     }
 
-    /// <summary>
-    /// Background loop that processes transactions from the handler.
-    /// </summary>
-    private async Task ProcessTransactionsAsync()
+    internal void SetHost(IHost host)
     {
-        await foreach (
-            var transaction in _transactionChannel.Reader.ReadAllAsync(_shutdownCts.Token)
-        )
-            try
-            {
-                await ProcessTransactionAsync(transaction);
-            }
-            catch (Exception ex)
-            {
-                // Fail the transaction and continue processing
-                transaction.Fail(ex);
-            }
+        ArgumentNullException.ThrowIfNull(host);
+        _host = host;
     }
 
-    /// <summary>
-    /// Routes a single transaction based on request type.
-    /// </summary>
-    private async Task ProcessTransactionAsync(LambdaHttpTransaction transaction)
+    internal HttpMessageHandler CreateHandler() =>
+        new LambdaTestingHttpHandler(_transactionChannel);
+
+    //      ┌──────────────────────────────────────────────────────────┐
+    //      │                        Public API                        │
+    //      └──────────────────────────────────────────────────────────┘
+
+    public async Task<InitResponse> StartAsync(CancellationToken cancellationToken = default)
     {
-        if (!_routeManager.TryMatch(transaction.Request, out var requestType, out var routeValues))
+        if (_state != ServerState.Created)
             throw new InvalidOperationException(
-                $"Unexpected request: {transaction.Request.Method} {transaction.Request.RequestUri}"
+                "TestServer has already been started and cannot be restarted."
             );
 
-        switch (requestType!.Value)
+        if (_host is null)
+            throw new InvalidOperationException("Host is not set.");
+
+        using var cts = LinkedCts(cancellationToken);
+
+        _state = ServerState.Starting;
+
+        _applicationLifetime = _host.Services.GetRequiredService<IHostApplicationLifetime>();
+
+        // Start the host
+        await _host.StartAsync(cts.Token);
+
+        // Start background processing
+        _processingTask = Task.Run(ProcessTransactionsAsync, cts.Token);
+
+        await TaskHelpers
+            .WhenAny(_processingTask, _entryPointCompletion, _initCompletionTcs.Task)
+            .UnwrapAndThrow("Exception(s) encountered while running StartAsync");
+
+        if (_entryPointCompletion.IsCompleted)
+            return new InitResponse { InitStatus = InitStatus.HostExited };
+
+        if (_initCompletionTcs.Task.IsCompleted)
         {
-            case RequestType.GetNextInvocation:
-                await HandleGetNextInvocationAsync(transaction);
-                break;
-
-            case RequestType.PostResponse:
-                await HandlePostResponseAsync(transaction, routeValues!);
-                break;
-
-            case RequestType.PostError:
-                await HandlePostErrorAsync(transaction, routeValues!);
-                break;
-
-            default:
-                throw new InvalidOperationException(
-                    $"Unexpected request type {requestType} for {transaction.Request.RequestUri}"
-                );
+            _state =
+                _initCompletionTcs.Task.Result.InitStatus == InitStatus.InitCompleted
+                    ? ServerState.Running
+                    : ServerState.Stopped;
+
+            return _initCompletionTcs.Task.Result;
         }
+
+        throw new InvalidOperationException(
+            "TestServer initialization failed with neither an error nor completion."
+        );
     }
 
-    /// <summary>
-    /// Handles GET /invocation/next - Bootstrap polling for work.
-    /// </summary>
-    private async Task HandleGetNextInvocationAsync(LambdaHttpTransaction transaction)
+    public async Task<InvocationResponse<TResponse>> InvokeAsync<TResponse, TEvent>(
+        TEvent invokeEvent,
+        string? traceId = null,
+        CancellationToken cancellationToken = default
+    )
     {
-        // Try to dequeue next pending invocation (FIFO)
-        if (!TryDequeuePendingInvocation(out var pending))
-        {
-            // No work available - queue this /next request
-            try
-            {
-                await _queuedNextRequests.Writer.WriteAsync(transaction, _shutdownCts.Token);
-            }
-            catch (OperationCanceledException)
-            {
-                transaction.Fail(DisposedException);
-            }
+        if (_state != ServerState.Running)
+            throw new InvalidOperationException(
+                "TestServer is not Running and as such an event cannot be invoked."
+            );
 
-            return;
-        }
+        using var cts = LinkedCtsWithInvocationDeadline(cancellationToken);
+
+        traceId ??= Guid.NewGuid().ToString();
+
+        // Generate unique request ID
+        var requestId = GetRequestId();
+
+        // Create the event response with Lambda headers
+        var eventResponse = CreateEventResponse(invokeEvent, requestId, traceId);
+        var deadlineUtc = DateTimeOffset.UtcNow.Add(_serverOptions.FunctionTimeout);
+
+        var pending = PendingInvocation.Create(requestId, eventResponse, deadlineUtc);
+
+        _pendingInvocations.AddRequired(requestId, pending);
+
+        if (!_pendingInvocationIds.Writer.TryWrite(requestId))
+            throw new InvalidOperationException("Failed to enqueue pending invocation");
+
+        var completion = await pending.ResponseTcs.Task.WaitAsync(cts.Token);
+
+        var responseMessage = completion.Request;
+        var wasSuccess = completion.RequestType == RequestType.PostResponse;
+
+        var response = wasSuccess
+            ? await (
+                responseMessage.Content?.ReadFromJsonAsync<TResponse>(
+                    _jsonSerializerOptions,
+                    cts.Token
+                ) ?? Task.FromResult<TResponse?>(default)
+            )
+            : default;
 
-        RespondToNextRequest(transaction, pending);
+        var error = !wasSuccess
+            ? await (
+                responseMessage.Content?.ReadFromJsonAsync<ErrorResponse>(
+                    _jsonSerializerOptions,
+                    cts.Token
+                ) ?? Task.FromResult<ErrorResponse?>(null)
+            )
+            : null;
+
+        return new InvocationResponse<TResponse>
+        {
+            WasSuccess = wasSuccess,
+            Response = response,
+            Error = error,
+        };
     }
 
-    /// <summary>
-    /// Responds to a /next request with a pending invocation.
-    /// </summary>
-    private void RespondToNextRequest(LambdaHttpTransaction transaction, PendingInvocation pending)
+    public async Task StopAsync(CancellationToken cancellationToken = default)
     {
-        // Respond with the event payload and Lambda headers
-        if (transaction.Respond(pending.EventResponse))
-            return;
+        if (_state != ServerState.Running)
+            throw new InvalidOperationException(
+                "TestServer is not running and as such cannot be stopped."
+            );
+
+        _state = ServerState.Stopping;
+
+        await _shutdownCts.CancelAsync();
+
+        _applicationLifetime.StopApplication();
+
+        await TaskHelpers
+            .WhenAll(_entryPointCompletion, _processingTask!)
+            .UnwrapAndThrow("Exception(s) encountered while running StopAsync")
+            .WaitAsync(cancellationToken);
 
-        // Request was already canceled; re-enqueue invocation to avoid dropping it
-        _pendingInvocationIds.Enqueue(pending.RequestId);
+        _state = ServerState.Stopped;
     }
 
-    /// <summary>
-    /// Responds to a /next request with a pending invocation by looking up the next one.
-    /// </summary>
-    private void RespondToNextRequest(LambdaHttpTransaction transaction)
+    //      ┌──────────────────────────────────────────────────────────┐
+    //      │                Internal TestServer Logic                 │
+    //      └──────────────────────────────────────────────────────────┘
+
+    private async Task ProcessTransactionsAsync()
     {
-        // Try to dequeue next pending invocation (FIFO)
-        if (!TryDequeuePendingInvocation(out var pending))
+        try
         {
-            // This shouldn't happen, but if it does, respond with error
-            transaction.Respond(
-                new HttpResponseMessage(HttpStatusCode.InternalServerError)
+            await foreach (
+                var transaction in _transactionChannel.Reader.ReadAllAsync(_shutdownCts.Token)
+            )
+            {
+                if (
+                    !_routeManager.TryMatch(
+                        transaction.Request,
+                        out var requestType,
+                        out var routeValues
+                    )
+                )
+                    throw new InvalidOperationException(
+                        $"Unexpected request received from the Lambda HTTP handler: {transaction.Request.Method} {transaction.Request.RequestUri}"
+                    );
+
+                switch (requestType!.Value)
                 {
-                    Content = new StringContent("No pending invocations available"),
+                    case RequestType.GetNextInvocation:
+                        await HandleGetNextInvocationAsync(transaction);
+                        break;
+
+                    case RequestType.PostResponse:
+                        await HandlePostResponseAsync(transaction, routeValues!);
+                        break;
+
+                    case RequestType.PostError:
+                        await HandlePostErrorAsync(transaction, routeValues!);
+                        break;
+
+                    case RequestType.PostInitError:
+                        await HandlePostInitErrorAsync(transaction);
+                        break;
+
+                    default:
+                        throw new InvalidOperationException(
+                            $"Unexpected request type {requestType} for {transaction.Request.RequestUri}"
+                        );
                 }
-            );
-            return;
+            }
         }
+        catch (OperationCanceledException) when (_shutdownCts.IsCancellationRequested)
+        {
+            // Expected when task is canceled
+        }
+    }
 
-        RespondToNextRequest(transaction, pending);
+    private async Task HandleGetNextInvocationAsync(LambdaHttpTransaction transaction)
+    {
+        if (_state == ServerState.Starting)
+            _initCompletionTcs.SetResult(
+                new InitResponse { InitStatus = InitStatus.InitCompleted }
+            );
+
+        if (await _pendingInvocationIds.Reader.WaitToReadAsync(_shutdownCts.Token))
+        {
+            var requestId = await _pendingInvocationIds.Reader.ReadAsync(_shutdownCts.Token);
+            _pendingInvocations.GetRequired(requestId, out var pendingInvocation);
+            transaction.ResponseTcs.SetResult(pendingInvocation.EventResponse);
+        }
     }
 
-    /// <summary>
-    /// Handles POST /invocation/{requestId}/response - successful function execution.
-    /// </summary>
     private async Task HandlePostResponseAsync(
         LambdaHttpTransaction transaction,
         RouteValueDictionary routeValues
     )
     {
-        var requestId = routeValues["requestId"]?.ToString();
+        _pendingInvocations.GetRequired(routeValues["requestId"]?.ToString(), out var pending);
 
-        if (
-            string.IsNullOrEmpty(requestId)
-            || !_pendingInvocations.TryRemove(requestId, out var pending)
-        )
-        {
-            transaction.Respond(
-                new HttpResponseMessage(HttpStatusCode.NotFound)
-                {
-                    Content = new StringContent(
-                        string.IsNullOrEmpty(requestId)
-                            ? "Missing requestId"
-                            : $"No pending invocation for request ID: {requestId}"
-                    ),
-                }
-            );
-            return;
-        }
+        // Acknowledge to Bootstrap
+        transaction.Respond(CreateSuccessResponse());
 
-        // Complete the invocation with the response from Bootstrap
         pending.ResponseTcs.SetResult(
             await CreateCompletionAsync(RequestType.PostResponse, transaction.Request)
         );
-
-        // Acknowledge to Bootstrap
-        transaction.Respond(
-            new HttpResponseMessage(HttpStatusCode.Accepted)
-            {
-                Content = new StringContent(
-                    JsonSerializer.Serialize(
-                        new Dictionary<string, string> { ["status"] = "success" },
-                        _jsonSerializerOptions
-                    ),
-                    Encoding.UTF8,
-                    "application/json"
-                ),
-                Version = Version.Parse("1.1"),
-            }
-        );
-
-        return;
     }
 
-    /// <summary>
-    /// Handles POST /invocation/{requestId}/error - function execution failed.
-    /// </summary>
     private async Task HandlePostErrorAsync(
         LambdaHttpTransaction transaction,
         RouteValueDictionary routeValues
     )
     {
-        var requestId = routeValues["requestId"]?.ToString();
+        _pendingInvocations.GetRequired(routeValues["requestId"]?.ToString(), out var pending);
 
-        if (
-            string.IsNullOrEmpty(requestId)
-            || !_pendingInvocations.TryRemove(requestId, out var pending)
-        )
-        {
-            transaction.Respond(
-                new HttpResponseMessage(HttpStatusCode.NotFound)
-                {
-                    Content = new StringContent(
-                        string.IsNullOrEmpty(requestId)
-                            ? "Missing requestId"
-                            : $"No pending invocation for request ID: {requestId}"
-                    ),
-                }
-            );
-            return;
-        }
+        // Acknowledge to Bootstrap
+        transaction.Respond(CreateSuccessResponse());
 
-        // Complete the invocation with the error response from Bootstrap
         pending.ResponseTcs.SetResult(
             await CreateCompletionAsync(RequestType.PostError, transaction.Request)
         );
+    }
 
-        // Acknowledge to Bootstrap
-        transaction.Respond(
-            new HttpResponseMessage(HttpStatusCode.Accepted)
-            {
-                Content = new StringContent(
-                    "{\"status\":\"success\"}",
-                    Encoding.UTF8,
-                    "application/json"
-                ),
-                Version = Version.Parse("1.1"),
-            }
-        );
+    private async Task HandlePostInitErrorAsync(LambdaHttpTransaction transaction)
+    {
+        if (_state == ServerState.Starting)
+            _initCompletionTcs.SetResult(
+                new InitResponse
+                {
+                    Error = await (
+                        transaction.Request.Content?.ReadFromJsonAsync<ErrorResponse>(
+                            _jsonSerializerOptions
+                        ) ?? Task.FromResult<ErrorResponse?>(null)
+                    ),
+                    InitStatus = InitStatus.InitError,
+                }
+            );
 
-        return;
+        throw new InvalidOperationException(
+            "TestServer is already started and as such an initialization error cannot be reported."
+        );
     }
 
-    private bool TryDequeuePendingInvocation(out PendingInvocation pendingInvocation)
+    private HttpResponseMessage CreateEventResponse<TEvent>(
+        TEvent invokeEvent,
+        string requestId,
+        string traceId
+    )
     {
-        var now = DateTimeOffset.UtcNow;
-
-        while (_pendingInvocationIds.TryDequeue(out var requestId))
+        var response = new HttpResponseMessage(HttpStatusCode.OK)
         {
-            if (_pendingInvocations.TryGetValue(requestId, out pendingInvocation))
-            {
-                if (pendingInvocation.DeadlineUtc <= now)
-                {
-                    if (_pendingInvocations.TryRemove(requestId, out var expiredInvocation))
-                        expiredInvocation.ResponseTcs.TrySetCanceled();
+            Content = new StringContent(
+                JsonSerializer.Serialize(invokeEvent, _jsonSerializerOptions),
+                Encoding.UTF8,
+                "application/json"
+            ),
+            Version = Version.Parse("1.1"),
+        };
 
-                    continue;
-                }
+        // Add standard HTTP headers
+        response.Headers.Date = new DateTimeOffset(DateTime.UtcNow, TimeSpan.Zero);
+        response.Headers.TransferEncodingChunked = true;
 
-                return true;
-            }
-        }
+        // Add custom Lambda runtime headers
+        var deadlineMs = DateTimeOffset
+            .UtcNow.Add(_serverOptions.FunctionTimeout)
+            .ToUnixTimeMilliseconds();
+        response.Headers.Add("Lambda-Runtime-Deadline-Ms", deadlineMs.ToString());
+        response.Headers.Add("Lambda-Runtime-Aws-Request-Id", requestId);
+        response.Headers.Add("Lambda-Runtime-Trace-Id", traceId);
+        response.Headers.Add("Lambda-Runtime-Invoked-Function-Arn", _serverOptions.FunctionArn);
 
-        pendingInvocation = null!;
-        return false;
-    }
+        // Add any additional custom headers
+        foreach (var header in _serverOptions.AdditionalHeaders)
+            response.Headers.Add(header.Key, header.Value);
 
-    private void CancelPendingInvocation(string requestId, CancellationToken cancellationToken)
-    {
-        if (_pendingInvocations.TryRemove(requestId, out var pendingInvocation))
-            pendingInvocation.ResponseTcs.TrySetCanceled(cancellationToken);
+        return response;
     }
 
+    private string GetRequestId() =>
+        Interlocked.Increment(ref _requestCounter).ToString().PadLeft(12, '0');
+
     private static async Task<InvocationCompletion> CreateCompletionAsync(
         RequestType requestType,
         HttpRequestMessage sourceRequest
@@ -416,4 +445,33 @@ HttpRequestMessage sourceRequest
 
         return new InvocationCompletion { Request = clonedRequest, RequestType = requestType };
     }
+
+    private static HttpResponseMessage CreateSuccessResponse() =>
+        new(HttpStatusCode.Accepted)
+        {
+            Content = new StringContent(
+                """
+                {"status":"success"}
+                """,
+                Encoding.UTF8,
+                "application/json"
+            ),
+            Version = Version.Parse("1.1"),
+        };
+
+    private CancellationTokenSource LinkedCtsWithInvocationDeadline(
+        CancellationToken cancellationTokens
+    )
+    {
+        var cts = CancellationTokenSource.CreateLinkedTokenSource(
+            cancellationTokens,
+            _shutdownCts.Token
+        );
+        cts.CancelAfter(_serverOptions.FunctionTimeout);
+
+        return cts;
+    }
+
+    private CancellationTokenSource LinkedCts(CancellationToken cancellationTokens) =>
+        CancellationTokenSource.CreateLinkedTokenSource(cancellationTokens, _shutdownCts.Token);
 }
diff --git a/src/AwsLambda.Host.Testing/LambdaTestServerExtensions.cs b/src/AwsLambda.Host.Testing/LambdaTestServerExtensions.cs
new file mode 100644
index 00000000..43220ecc
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/LambdaTestServerExtensions.cs
@@ -0,0 +1,16 @@
+namespace AwsLambda.Host.Testing;
+
+public static class LambdaTestServerExtensions
+{
+    extension(LambdaTestServer server)
+    {
+        public Task<InvocationResponse<TResponse>> InvokeAsync<TResponse, TEvent>(
+            TEvent invokeEvent,
+            CancellationToken cancellationToken = default
+        ) =>
+            server.InvokeAsync<TResponse, TEvent>(
+                invokeEvent,
+                cancellationToken: cancellationToken
+            );
+    }
+}
diff --git a/src/AwsLambda.Host.Testing/Models/ErrorResponse.cs b/src/AwsLambda.Host.Testing/Models/ErrorResponse.cs
index 540fd345..02fb088c 100644
--- a/src/AwsLambda.Host.Testing/Models/ErrorResponse.cs
+++ b/src/AwsLambda.Host.Testing/Models/ErrorResponse.cs
@@ -13,6 +13,9 @@ public class ErrorResponse
     [JsonPropertyName("cause")]
     public ErrorCause? Cause { get; set; }
 
+    [JsonPropertyName("causes")]
+    public List<ErrorCause>? Causes { get; set; }
+
     /// <summary>
     /// The error message describing what went wrong.
     /// </summary>
diff --git a/src/AwsLambda.Host.Testing/Models/InitResponse.cs b/src/AwsLambda.Host.Testing/Models/InitResponse.cs
new file mode 100644
index 00000000..833a22b2
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/Models/InitResponse.cs
@@ -0,0 +1,17 @@
+namespace AwsLambda.Host.Testing;
+
+/// <summary>
+/// Represents the result of a Lambda function initialization attempt.
+/// </summary>
+public class InitResponse
+{
+    /// <summary>
+    /// Gets the error information if initialization failed, or null if initialization succeeded.
+    /// </summary>
+    public ErrorResponse? Error { get; internal init; }
+
+    /// <summary>
+    /// Gets the status of the initialization attempt.
+    /// </summary>
+    public InitStatus InitStatus { get; internal init; }
+}
diff --git a/src/AwsLambda.Host.Testing/Models/InitStatus.cs b/src/AwsLambda.Host.Testing/Models/InitStatus.cs
new file mode 100644
index 00000000..eee1c0ff
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/Models/InitStatus.cs
@@ -0,0 +1,22 @@
+namespace AwsLambda.Host.Testing;
+
+/// <summary>
+/// An enumeration of possible statuses for Lambda initialization.
+/// </summary>
+public enum InitStatus
+{
+    /// <summary>
+    /// Initialization of the Lambda completed successfully.
+    /// </summary>
+    InitCompleted,
+
+    /// <summary>
+    /// Initialization of the Lambda failed, and the Lambda returned an error.
+    /// </summary>
+    InitError,
+
+    /// <summary>
+    /// Initialization of the Lambda failed, and the Host process exited.
+    /// </summary>
+    HostExited,
+}
diff --git a/src/AwsLambda.Host.Testing/Models/LambdaBootstrapRequest.cs b/src/AwsLambda.Host.Testing/Models/LambdaBootstrapRequest.cs
deleted file mode 100644
index b4ab21da..00000000
--- a/src/AwsLambda.Host.Testing/Models/LambdaBootstrapRequest.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Microsoft.AspNetCore.Routing;
-
-namespace AwsLambda.Host.Testing;
-
-internal class LambdaBootstrapRequest
-{
-    internal string? RequestId
-    {
-        get
-        {
-            field ??=
-                RouteValue.TryGetValue("RequestId", out var requestId)
-                && requestId is string requestIdString
-                    ? requestIdString
-                    : null;
-
-            return field;
-        }
-    }
-
-    internal required HttpRequestMessage RequestMessage { get; init; }
-    internal required RequestType RequestType { get; init; }
-    internal required RouteValueDictionary RouteValue { get; init; }
-}
diff --git a/src/AwsLambda.Host.Testing/Models/RequestType.cs b/src/AwsLambda.Host.Testing/Models/RequestType.cs
index 935bd790..49108da9 100644
--- a/src/AwsLambda.Host.Testing/Models/RequestType.cs
+++ b/src/AwsLambda.Host.Testing/Models/RequestType.cs
@@ -5,4 +5,5 @@ internal enum RequestType
     GetNextInvocation,
     PostResponse,
     PostError,
+    PostInitError,
 }
diff --git a/src/AwsLambda.Host.Testing/Models/ServerState.cs b/src/AwsLambda.Host.Testing/Models/ServerState.cs
new file mode 100644
index 00000000..892b610d
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/Models/ServerState.cs
@@ -0,0 +1,37 @@
+namespace AwsLambda.Host.Testing;
+
+/// <summary>
+/// Represents the lifecycle state of a LambdaTestServer.
+/// </summary>
+public enum ServerState
+{
+    /// <summary>
+    /// TestServer created but not started.
+    /// </summary>
+    Created,
+
+    /// <summary>
+    /// TestServer is starting (building host).
+    /// </summary>
+    Starting,
+
+    /// <summary>
+    /// TestServer is running and accepting invocations.
+    /// </summary>
+    Running,
+
+    /// <summary>
+    /// TestServer is stopping.
+    /// </summary>
+    Stopping,
+
+    /// <summary>
+    /// TestServer has stopped cleanly.
+    /// </summary>
+    Stopped,
+
+    /// <summary>
+    /// TestServer has been disposed.
+    /// </summary>
+    Disposed,
+}
diff --git a/src/AwsLambda.Host.Testing/Options/LambdaClientOptions.cs b/src/AwsLambda.Host.Testing/Options/LambdaClientOptions.cs
deleted file mode 100644
index 025719b1..00000000
--- a/src/AwsLambda.Host.Testing/Options/LambdaClientOptions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace AwsLambda.Host.Testing;
-
-/// <summary>
-/// Configuration options for the Lambda test client.
-/// </summary>
-public class LambdaClientOptions
-{
-    /// <summary>
-    /// Gets or sets the headers to include in Lambda invocation responses.
-    /// </summary>
-    public LambdaInvocationHeaderOptions InvocationHeaderOptions { get; set; } = new();
-}
diff --git a/src/AwsLambda.Host.Testing/Options/LambdaInvocationHeaderOptions.cs b/src/AwsLambda.Host.Testing/Options/LambdaInvocationHeaderOptions.cs
deleted file mode 100644
index 45379cc0..00000000
--- a/src/AwsLambda.Host.Testing/Options/LambdaInvocationHeaderOptions.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace AwsLambda.Host.Testing;
-
-/// <summary>
-/// Headers returned in Lambda runtime API invocation responses.
-/// </summary>
-public class LambdaInvocationHeaderOptions
-{
-    /// <summary>
-    /// Gets or sets additional custom headers to include in the Lambda invocation response.
-    /// Use this to add any additional headers beyond the standard Lambda runtime headers.
-    /// </summary>
-    public Dictionary<string, string> AdditionalHeaders { get; set; } = new();
-
-    /// <summary>
-    /// Gets or sets the response date header.
-    /// Maps to the <c>Date</c> HTTP header. Defaults to current UTC time.
-    /// </summary>
-    public DateTime Date { get; set; } = DateTime.UtcNow;
-
-    /// <summary>
-    /// Gets or sets the ARN of the Lambda function being invoked.
-    /// Maps to the <c>Lambda-Runtime-Invoked-Function-Arn</c> header.
-    /// </summary>
-    public string FunctionArn { get; set; } =
-        "arn:aws:lambda:us-west-2:123412341234:function:Function";
-
-    /// <summary>
-    /// Gets or sets the Lambda function timeout duration.
-    /// Maps to the <c>Lambda-Runtime-Deadline-Ms</c> header as Unix epoch milliseconds.
-    /// This determines when the Lambda function will timeout, calculated as the current time plus this duration.
-    /// Defaults to 15 minutes.
-    /// </summary>
-    public TimeSpan FunctionTimeout { get; set; } = TimeSpan.FromMinutes(15);
-
-    /// <summary>
-    /// Gets or sets the maximum amount of time to wait for a response from the Lambda bootstrap.
-    /// Defaults to the function timeout plus a small buffer.
-    /// </summary>
-    public TimeSpan ClientWaitTimeout { get; set; } =
-        TimeSpan.FromMinutes(15).Add(TimeSpan.FromSeconds(5));
-
-    /// <summary>
-    /// Legacy property for client wait timeout. Prefer <see cref="ClientWaitTimeout"/>.
-    /// </summary>
-    [Obsolete("Use ClientWaitTimeout instead.")]
-    public TimeSpan InvocationTimeout
-    {
-        get => ClientWaitTimeout;
-        set => ClientWaitTimeout = value;
-    }
-
-    /// <summary>
-    /// Gets or sets the AWS X-Ray trace ID for distributed tracing.
-    /// Maps to the <c>Lambda-Runtime-Trace-Id</c> header.
-    /// </summary>
-    public string TraceId { get; set; } = Guid.NewGuid().ToString();
-
-    /// <summary>
-    /// Gets or sets whether to use chunked transfer encoding.
-    /// Maps to the <c>Transfer-Encoding: chunked</c> HTTP header. Defaults to true.
-    /// </summary>
-    public bool TransferEncodingChunked { get; set; } = true;
-}
diff --git a/src/AwsLambda.Host.Testing/Options/LambdaServerOptions.cs b/src/AwsLambda.Host.Testing/Options/LambdaServerOptions.cs
new file mode 100644
index 00000000..2b92f9a2
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/Options/LambdaServerOptions.cs
@@ -0,0 +1,28 @@
+namespace AwsLambda.Host.Testing;
+
+/// <summary>
+/// Configuration options for the Lambda test client.
+/// </summary>
+public class LambdaServerOptions
+{
+    /// <summary>
+    /// Gets or sets additional custom headers to include in the Lambda invocation response.
+    /// Use this to add any additional headers beyond the standard Lambda runtime headers.
+    /// </summary>
+    public Dictionary<string, string> AdditionalHeaders { get; set; } = new();
+
+    /// <summary>
+    /// Gets or sets the ARN of the Lambda function being invoked.
+    /// Maps to the <c>Lambda-Runtime-Invoked-Function-Arn</c> header.
+    /// </summary>
+    public string FunctionArn { get; set; } =
+        "arn:aws:lambda:us-west-2:123412341234:function:Function";
+
+    /// <summary>
+    /// Gets or sets the Lambda function timeout duration.
+    /// Maps to the <c>Lambda-Runtime-Deadline-Ms</c> header as Unix epoch milliseconds.
+    /// This determines when the Lambda function will timeout, calculated as the current time plus this duration.
+    /// Defaults to 15 minutes.
+    /// </summary>
+    public TimeSpan FunctionTimeout { get; set; } = TimeSpan.FromMinutes(15);
+}
diff --git a/src/AwsLambda.Host.Testing/README.md b/src/AwsLambda.Host.Testing/README.md
index afb866ba..547ccc1c 100644
--- a/src/AwsLambda.Host.Testing/README.md
+++ b/src/AwsLambda.Host.Testing/README.md
@@ -178,6 +178,37 @@ Headers:
 
 ```
 
+```json
+{
+  "errorType": "AggregateException",
+  "errorMessage": "Encountered errors while running OnInit handlers: (Init failed)",
+  "stackTrace": [
+    "at AwsLambda.Host.Builder.LambdaOnInitBuilder.<Build>b__10_1(CancellationToken stoppingToken) in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/src/AwsLambda.Host/Builder/LambdaOnInitBuilder.cs:line 65",
+    "at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InitializeAsync()"
+  ],
+  "cause": {
+    "errorType": "Exception",
+    "errorMessage": "Init failed",
+    "stackTrace": [
+      "at Program.<>c.<<Main>$>b__0_1() in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/examples/AwsLambda.Host.Example.Testing/Lambda/Program.cs:line 18",
+      "at AwsLambda.Host.Core.Generated.<LambdaHandler_g>F0316F908774B11B609C941996D283BB313AD08F671E39F0BE1901FEE2BBA6D4C__GeneratedLambdaOnInitBuilderExtensions.<>c__DisplayClass0_0.<OnInitInterceptor0>g__OnInit|0(IServiceProvider serviceProvider, CancellationToken cancellationToken) in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/examples/AwsLambda.Host.Example.Testing/Lambda/obj/Debug/net10.0/AwsLambda.Host.SourceGenerators/AwsLambda.Host.SourceGenerators.MapHandlerIncrementalGenerator/LambdaHandler.g.cs:line 109",
+      "at AwsLambda.Host.Builder.LambdaOnInitBuilder.RunInitHandler(LambdaInitDelegate handler, CancellationToken cancellationToken) in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/src/AwsLambda.Host/Builder/LambdaOnInitBuilder.cs:line 81"
+    ]
+  },
+  "causes": [
+    {
+      "errorType": "Exception",
+      "errorMessage": "Init failed",
+      "stackTrace": [
+        "at Program.<>c.<<Main>$>b__0_1() in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/examples/AwsLambda.Host.Example.Testing/Lambda/Program.cs:line 18",
+        "at AwsLambda.Host.Core.Generated.<LambdaHandler_g>F0316F908774B11B609C941996D283BB313AD08F671E39F0BE1901FEE2BBA6D4C__GeneratedLambdaOnInitBuilderExtensions.<>c__DisplayClass0_0.<OnInitInterceptor0>g__OnInit|0(IServiceProvider serviceProvider, CancellationToken cancellationToken) in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/examples/AwsLambda.Host.Example.Testing/Lambda/obj/Debug/net10.0/AwsLambda.Host.SourceGenerators/AwsLambda.Host.SourceGenerators.MapHandlerIncrementalGenerator/LambdaHandler.g.cs:line 109",
+        "at AwsLambda.Host.Builder.LambdaOnInitBuilder.RunInitHandler(LambdaInitDelegate handler, CancellationToken cancellationToken) in /Users/jonasha/Repos/CSharp/dotnet-lambda-host/src/AwsLambda.Host/Builder/LambdaOnInitBuilder.cs:line 81"
+      ]
+    }
+  ]
+}
+```
+
 # In-Memory Lambda Testing Client Implementation Summary
 
 ## Overview
diff --git a/src/AwsLambda.Host.Testing/TaskHelpers.cs b/src/AwsLambda.Host.Testing/TaskHelpers.cs
new file mode 100644
index 00000000..8d23fd5c
--- /dev/null
+++ b/src/AwsLambda.Host.Testing/TaskHelpers.cs
@@ -0,0 +1,36 @@
+namespace AwsLambda.Host.Testing;
+
+internal static class TaskHelpers
+{
+    internal static async Task<Exception[]> WhenAny(params Task[] tasks)
+    {
+        await Task.WhenAny(tasks);
+        return ExtractExceptions(tasks);
+    }
+
+    internal static async Task<Exception[]> WhenAll(params Task[] tasks)
+    {
+        await Task.WhenAll(tasks);
+        return ExtractExceptions(tasks);
+    }
+
+    private static Exception[] ExtractExceptions(Task[] tasks) =>
+        tasks
+            .Where(t => t is { IsFaulted: true, Exception: not null })
+            .Select(e =>
+                e.Exception!.InnerExceptions.Count > 1
+                    ? e.Exception
+                    : e.Exception.InnerExceptions[0]
+            )
+            .ToArray();
+
+    extension(Task<Exception[]> exceptionsTask)
+    {
+        internal async Task UnwrapAndThrow(string errorMessage)
+        {
+            var exceptions = await exceptionsTask;
+            if (exceptions.Length > 0)
+                throw new AggregateException(errorMessage, exceptions);
+        }
+    }
+}
diff --git a/src/AwsLambda.Host/Core/Context/DefaultLambdaHostContext.cs b/src/AwsLambda.Host/Core/Context/DefaultLambdaHostContext.cs
index b405ebe9..4c07e181 100644
--- a/src/AwsLambda.Host/Core/Context/DefaultLambdaHostContext.cs
+++ b/src/AwsLambda.Host/Core/Context/DefaultLambdaHostContext.cs
@@ -69,6 +69,10 @@ public async ValueTask DisposeAsync()
 
     public TimeSpan RemainingTime => _lambdaContext.RemainingTime;
 
+    public string TenantId => _lambdaContext.TenantId;
+
+    public string TraceId => _lambdaContext.TraceId;
+
     //      ┌──────────────────────────────────────────────────────────┐
     //      │                    ILambdaHostContext                    │
     //      └──────────────────────────────────────────────────────────┘
