diff --git a/build.zig b/build.zig
index 24cd2fe..cfe4839 100644
--- a/build.zig
+++ b/build.zig
@@ -1,5 +1,5 @@
 const std = @import("std");
-const htzx = @import("build/htzx_build.zig");
+const htzx = @import("src/htzx/htzx_build.zig");
 pub fn build(b: *std.Build) !void {
     const target = b.standardTargetOptions(.{});
     const optimize = b.standardOptimizeOption(.{});
diff --git a/src/lib/users/user_routes.zig b/build.zig.zon
similarity index 100%
rename from src/lib/users/user_routes.zig
rename to build.zig.zon
diff --git a/src/htzx/html_parser/scanner.zig b/src/htzx/html_parser/scanner.zig
new file mode 100644
index 0000000..e4eb5c4
--- /dev/null
+++ b/src/htzx/html_parser/scanner.zig
@@ -0,0 +1,21 @@
+const std = @import("std");
+
+const Token = @import("token.zig").Token;
+
+pub fn Scanner(comptime Source: type) type {
+    return struct {
+        const Self = @This();
+        const SourceType = Source;
+        line: u32,
+        col: u32,
+        current: u32,
+
+        // fn scanArrayList(self: Self, source: SourceType, token_array: std.ArrayListAligned(Token, null)) !std.ArrayListAligned(Token, null) {
+        //     _ = self;
+        //     var eof = false;
+        //     while (!eof) {
+        //         // switch(
+        //     }
+        // }
+    };
+} 
diff --git a/src/htzx/html_parser/token.zig b/src/htzx/html_parser/token.zig
new file mode 100644
index 0000000..a9edf68
--- /dev/null
+++ b/src/htzx/html_parser/token.zig
@@ -0,0 +1,82 @@
+const std = @import("std");
+
+pub const Token = struct {
+    lexeme: Lexeme,
+    raw: []const u8,
+    line: u32,
+    col: u32,
+};
+
+pub const Lexeme = enum {
+    NEWLINE,
+    LT,
+    GT,
+    CLOSING_LT,
+    EQUALS,
+    TAG,
+	SINGLE_QUOTE,
+	DOUBLE_QOUTE,
+	TEXT,
+	DOUBLE_OPEN_BRACE,
+	DOUBLE_CLOSE_BRACE,
+};
+
+pub const TagTypes = enum {
+    a,
+    abbr,
+    blockquote,
+    body,
+    br,
+    button,
+    canvas,
+    caption,
+    code,
+    div,
+    em,
+    embed,
+    form,
+    frame,
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6,
+    head,
+    header,
+    html,
+    iframe,
+    img,
+    input,
+    label,
+    main,
+    nav,
+    object,
+    option,
+    p,
+    picture,
+    script,
+    section,
+    source,
+    span,
+    strong,
+    style,
+    table,
+    template,
+    title,
+    video,
+};
+
+pub const TagAttributes = enum {
+    href,
+    alt,
+    border,
+    name,
+    src,
+    style,
+    class,
+    maxlength,
+    name,
+    onblur
+};
+// <h3 class="gogo">text</h3>
diff --git a/build/htzx_build.zig b/src/htzx/htzx_build.zig
similarity index 83%
rename from build/htzx_build.zig
rename to src/htzx/htzx_build.zig
index ec32ee9..fd7fd64 100644
--- a/build/htzx_build.zig
+++ b/src/htzx/htzx_build.zig
@@ -4,8 +4,10 @@ const log = std.log.scoped(.htzx_comp);
 pub fn build_htzx(b: *std.Build, exe: *std.Build.Step.Compile) !void {
     // Setup comptime parsing for /lib files
     var markup_files = std.ArrayList([]const u8).init(b.allocator);
+    var markup_files_content = std.ArrayList([]const u8).init(b.allocator);
     // var zig_files = std.ArrayList([]const u8).init(b.allocator);
     defer markup_files.deinit();
+    defer markup_files_content.deinit();
     // defer zig_files.deinit();
 
     var options = b.addOptions();
@@ -27,7 +29,7 @@ pub fn build_htzx(b: *std.Build, exe: *std.Build.Step.Compile) !void {
         }
         if (file.?.kind == .file) {
             const relative_path = file.?.path;
-            const file_path = try std.mem.concat(b.allocator, u8, &[_][]const u8{lib_dir_path, relative_path});
+            const file_path = try std.mem.concat(b.allocator, u8, &[_][]const u8{ lib_dir_path, relative_path });
             var split_iter = std.mem.splitBackwardsAny(u8, relative_path, ".");
             var extension = split_iter.first();
             // if (std.mem.eql(u8, extension, "zig")) {
@@ -49,8 +51,11 @@ pub fn build_htzx(b: *std.Build, exe: *std.Build.Step.Compile) !void {
             if (std.mem.eql(u8, extension, "html")) {
                 log.info("Adding html file {s}...", .{file_path});
                 try markup_files.append(b.dupe(file_path));
+                var f = try std.fs.cwd().openFile(file_path, .{});
+                var content = try f.readToEndAlloc(b.allocator, 1024 * 1024);
+                try markup_files_content.append(content);
             } else {
-               log.info("Unrecognized file extension '.{s}'\t{s}", .{ extension, file_path });
+                log.info("Unrecognized file extension '.{s}'\t{s}", .{ extension, file_path });
             }
         }
         // std.debug.print("Got entry in `versions:` base: {s}, path: {s}, kind: {any}\n", .{ ver.?.basename, ver.?.path, ver.?.kind });
@@ -60,5 +65,6 @@ pub fn build_htzx(b: *std.Build, exe: *std.Build.Step.Compile) !void {
     // as a string array at comptime in main.zig
     // options.addOption([]const []const u8, "zig_files", zig_files.items);
     options.addOption([]const []const u8, "markup_files", markup_files.items);
+    options.addOption([]const []const u8, "markup_files_content", markup_files_content.items);
     exe.addOptions("options", options);
 }
diff --git a/src/htzx/method_route.zig b/src/htzx/method_route.zig
new file mode 100644
index 0000000..c6658ec
--- /dev/null
+++ b/src/htzx/method_route.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+const string = []const u8;
+
+/// Tagged union `std.http.Method` with string route
+pub const MethodRoute = union(std.http.Method) {
+    GET: string,
+    HEAD: string,
+    POST: string,
+    PUT: string,
+    DELETE: string,
+    CONNECT: string,
+    OPTIONS: string,
+    TRACE: string,
+    PATCH: string,
+
+    pub fn create(method: std.http.Method, target: string) MethodRoute {
+        var method_route = switch (method) {
+            .GET => MethodRoute{ .GET = target },
+            .POST => MethodRoute{ .POST = target },
+            .PUT => MethodRoute{ .PUT = target },
+            .DELETE => MethodRoute{ .DELETE = target },
+            .PATCH => MethodRoute{ .PATCH = target },
+            else => unreachable,
+        };
+        return method_route;
+    }
+
+    pub fn getRoute(method_route: MethodRoute) string {
+        var route = switch (method_route) {
+            .GET => method_route.GET,
+            .POST => method_route.POST,
+            .PUT => method_route.PUT,
+            .DELETE => method_route.DELETE,
+            .PATCH => method_route.PATCH,
+            else => unreachable,
+        };
+        return route;
+    }
+
+    pub fn getMethod(method_route: MethodRoute) std.http.Method {
+        var http_method = switch (method_route) {
+            .GET => std.http.Method.GET,
+            .POST => std.http.Method.POST,
+            .PUT => std.http.Method.PUT,
+            .DELETE => std.http.Method.DELETE,
+            .PATCH => std.http.Method.PATCH,
+            else => unreachable,
+        };
+        return http_method;
+    }
+};
+
+/// Route Context for creating a std.ArrayHashMap with MethodRoute keys
+pub const RouteContext = struct {
+    pub fn hash(self: @This(), key: MethodRoute) u32 {
+        _ = self;
+        var route = key.getRoute();
+        return std.array_hash_map.hashString(route);
+    }
+
+    pub fn eql(self: @This(), a: MethodRoute, b: MethodRoute, b_index: usize) bool {
+        _ = b_index;
+        _ = self;
+        return std.mem.eql(u8, a.getRoute(), b.getRoute()) and a.getMethod() == b.getMethod();
+    }
+};
diff --git a/src/htzx/utils.zig b/src/htzx/utils.zig
index 5733615..04f5a17 100644
--- a/src/htzx/utils.zig
+++ b/src/htzx/utils.zig
@@ -1,17 +1,66 @@
 const std = @import("std");
 const one_megabyte: usize = 1024 * 1024;
 const string = []const u8;
+const log = std.log.scoped(.server);
+const HtmlValidator = @import("validators.zig").HtmlValidator;
+const markup_files_content = @import("options").markup_files_content;
+
+pub const MarkupContent = struct {
+    path: string,
+    html: string,
+};
 
 // Caller owns the returned StringArrayHashMap
-pub fn open_html_load(allocator: std.mem.Allocator, filenames: []const []const u8) !std.StringArrayHashMap(string) {
+pub fn open_validate_html_load(allocator: std.mem.Allocator, comptime filenames: []const []const u8) !std.StringArrayHashMap(string) {
     var dir = std.fs.cwd();
     var html_file_map = std.StringArrayHashMap(string).init(allocator);
 
-    for (filenames) |file_name| {
+    comptime var index = 0;
+    inline for (filenames) |file_name| {
+        comptime {
+            // Validate comptime html file content
+            var html_content = markup_files_content[index];
+            var valid = HtmlValidator.comp_validate(html_content);
+            if (!valid) {
+                @compileError("Invalid html in file " ++ file_name);
+            }
+            index += 1;
+        }
+        // Get rid of "src/lib"
+        var file_base = file_name[7..];
+        var iter = std.mem.splitBackwardsSequence(u8, file_base, "/");
+        var basename = iter.first();
+        var route_len = file_base.len - basename.len;
+        var arr: [100]u8 = undefined;
+        var buf = arr[0..];
+        var route_name = try std.fmt.bufPrint(buf, "{s}", .{file_base[0..route_len]});
         var f = try dir.openFile(file_name, .{});
         var html_content = try f.readToEndAlloc(allocator, one_megabyte);
-        try html_file_map.putNoClobber(file_name, html_content);
+        // log.info("Adding html get route to {s}\tGot file_base {s}, basename {s}, route_len {d}...", .{
+        //     route_name,
+        //     file_base,
+        //     basename,
+        //     route_len,
+        // });
+        try html_file_map.putNoClobber(try allocator.dupe(u8, route_name), html_content);
     }
 
     return html_file_map;
 }
+
+// pub fn nameToEnumTag(name: []const u8, comptime Enum: type) enumError!Enum {
+//     comptime {
+//         if (@typeInfo(Enum) != .Enum) {
+//             @compileError("Non enum type passed into function: " ++ @typeName(Enum));
+//         }
+//     }
+
+//     inline for (@typeInfo(Enum).Enum.fields) |field| {
+//         if (std.mem.eql(u8, name, field.name)) {
+//             const tag: Enum = @enumFromInt(field.value);
+//             return tag;
+//         }
+//     }
+//     return enumError.EnumFieldNotFound;
+// }
+
diff --git a/src/htzx/validators.zig b/src/htzx/validators.zig
new file mode 100644
index 0000000..9fa3382
--- /dev/null
+++ b/src/htzx/validators.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+
+
+pub const HtmlValidator = struct {
+
+    pub fn  comp_validate(comptime html: []const u8) bool {
+        _ = html;
+        // TODO this needs to be done
+        return true;
+    }
+
+    pub fn validate(html: []const u8) bool {
+        _ = html;
+
+        return true;
+    }
+};
diff --git a/src/lib/index.html b/src/lib/index.html
index e69de29..bcfa5fb 100644
--- a/src/lib/index.html
+++ b/src/lib/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>My Simple HTML Page</title>
+</head>
+<script src="https://unpkg.com/htmx.org@1.9.8"></script>
+<body>
+    <h1>Welcome to My Web Page</h1>
+    <p>This is a simple HTML document.</p>
+    <p>Thank you for visiting.</p>
+    <div id="parent-div">
+        <h3>Want Zig Facts?</h3>
+        <button hx-get="/zig"
+            hx-trigger="click"
+            hx-target="#parent-div"
+            hx-swap="outerHTML"
+        >
+           Get Zig Facts
+        </button>
+    </div>
+</body>
+</html>
diff --git a/src/lib/routes.zig b/src/lib/routes.zig
deleted file mode 100644
index 9be76f1..0000000
--- a/src/lib/routes.zig
+++ /dev/null
@@ -1,6 +0,0 @@
-const std = @import("std");
-
-pub fn post() i32 {
-    std.debug.print("Running route get!\n", .{});
-    return 13;
-}
diff --git a/src/main.zig b/src/main.zig
index 2bcaedf..9d4d8c2 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -14,7 +14,8 @@ pub fn main() !void {
     var server = Zx.init(allocator);
     defer server.deinit();
 
-    try server.GET("/zig", index.getIndex);
+    try server.GET("/zig", index.getZig);
+    try server.POST("/zig", index.postZig);
 
     server.runServer() catch |err| {
         log.err("Server exited with err: {any}\nDumping trace:\n", .{err});
diff --git a/src/routes/index.zig b/src/routes/index.zig
index 2a0d621..909eeae 100644
--- a/src/routes/index.zig
+++ b/src/routes/index.zig
@@ -1,11 +1,44 @@
 const std = @import("std");
 const http = std.http;
 const zx = @import("../zx.zig");
+const log = std.log.scoped(.index);
 
-pub fn getIndex(ctz: *zx.ZxContext) zx.ZxError!void {
-    ctz.json(.{ .success = true, .message = "hello did you get this?", .age = 1 }) catch |err| {
-        std.debug.print("Got error {any}\n", .{err});
+pub fn getZig(ctz: *zx.ZxContext) zx.ZxError!void {
+    ctz.html(
+    \\<ul>
+    \\<li>Zig is awesome</li>
+    \\<li>Zig runs everywhere</li>
+    \\<li>Zig has a cool name</li>
+    \\</ul>
+    , .ok) catch |err| {
+        log.err("Got error {any}\n", .{err});
         return zx.ZxError.Unexpected;
     };
     return;
 }
+
+const ZigPost = struct {
+    id: u32,
+    name: []const u8,
+};
+
+pub fn postZig(ctz: *zx.ZxContext) zx.ZxError!void {
+    if (ctz.req_body.len > 0) {
+        var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+        defer arena.deinit();
+
+        var json_body = std.json.parseFromSliceLeaky(ZigPost, arena.allocator(), ctz.req_body, .{}) catch |err| {
+            log.err("Error while parsing json: {any}", .{err});
+            return zx.ZxError.Unexpected;
+        };
+        log.info("Got id {d} name {s}", .{ json_body.id, json_body.name });
+        try ctz.json(.{ .success = true }, .ok);
+    } else {
+        ctz.json(.{ .success = false, .message = "Invalid request" }, .bad_request) catch |err| {
+            log.err("Got error {any}\n", .{err});
+            return zx.ZxError.Unexpected;
+        };
+    }
+
+    return;
+}
diff --git a/src/zx.zig b/src/zx.zig
index 769a395..732f96e 100644
--- a/src/zx.zig
+++ b/src/zx.zig
@@ -1,11 +1,20 @@
 const std = @import("std");
 const options = @import("options");
-const zx_utils = @import("htzx/utils.zig");
-const markup_files = options.markup_files;
 const http = std.http;
 const log = std.log.scoped(.server);
 
+const zx_utils = @import("htzx/utils.zig");
+
+const MethodRoute = @import("htzx/method_route.zig").MethodRoute;
+const RouteContext = @import("htzx/method_route.zig").RouteContext;
+const validators = @import("htzx/validators.zig");
+const HtmlValidator = validators.HtmlValidator;
+
+const markup_files = options.markup_files;
+
+// Types
 const string = []const u8;
+pub const ZxRouteFn = *const fn (*ZxContext) ZxError!void;
 
 const server_addr = "127.0.0.1";
 const server_port = 8000;
@@ -18,19 +27,17 @@ pub const ZxError = error{
     Unexpected,
 };
 
-pub const ZxRouteFn = *const fn (*ZxContext) ZxError!void;
-
 pub const Zx = struct {
     server: http.Server,
     allocator: std.mem.Allocator,
-    routes: std.StringArrayHashMap(ZxRouteFn),
+    routes: std.ArrayHashMap(MethodRoute, ZxRouteFn, RouteContext, true),
     html_files_map: std.StringArrayHashMap(string),
 
     pub fn init(
         allocator: std.mem.Allocator,
     ) Zx {
         var server = http.Server.init(allocator, .{ .reuse_address = true });
-        var routes = std.StringArrayHashMap(ZxRouteFn).init(allocator);
+        var routes = std.ArrayHashMap(MethodRoute, ZxRouteFn, RouteContext, true).init(allocator);
         var html_files_map = std.StringArrayHashMap(string).init(allocator);
         return .{ .server = server, .allocator = allocator, .routes = routes, .html_files_map = html_files_map };
     }
@@ -40,20 +47,19 @@ pub const Zx = struct {
         zx.routes.deinit();
 
         // free html file content values
-        for (zx.html_files_map.keys()) |key| {
-            var entry = zx.html_files_map.getEntry(key);
-            zx.allocator.free(entry.?.value_ptr);
-        }
+        zx.allocator.free(zx.html_files_map.keys());
+        zx.allocator.free(zx.html_files_map.values());
+        // for (zx.html_files_map.keys()) |key| {
+        //     var entry = zx.html_files_map.getEntry(key);
+        //     zx.allocator.destroy(entry.?.value_ptr);
+        // }
         zx.html_files_map.deinit();
     }
 
     pub fn runServer(zx: *Zx) !void {
         log.info("Server is running at {s}:{d}", .{ server_addr, server_port });
 
-        for (markup_files) |file| {
-            log.info("{s}", .{file});
-        }
-        zx.html_files_map = try zx_utils.open_html_load(zx.allocator, markup_files);
+        zx.html_files_map = try zx_utils.open_validate_html_load(zx.allocator, markup_files);
 
         // Parse the server address.
         const address = try std.net.Address.parseIp(server_addr, server_port);
@@ -80,13 +86,15 @@ pub const Zx = struct {
         }
     }
 
-    pub fn POST(comptime path: string, handler: ZxRouteFn) void {
-        _ = handler;
-        _ = path;
+    pub fn POST(zx: *Zx, path: string, handler: ZxRouteFn) ZxError!void {
+        zx.routes.putNoClobber(MethodRoute.create(.POST, path), handler) catch |err| {
+            std.debug.print("{any}\nCant add another route at {s}\n", .{ err, path });
+            return ZxError.RedefinedRoute;
+        };
     }
 
-    pub fn GET(zx: *Zx, comptime path: string, handler: ZxRouteFn) ZxError!void {
-        zx.routes.putNoClobber(path, handler) catch |err| {
+    pub fn GET(zx: *Zx, path: string, handler: ZxRouteFn) ZxError!void {
+        zx.routes.putNoClobber(MethodRoute.create(.GET, path), handler) catch |err| {
             std.debug.print("{any}\nCant add another route at {s}\n", .{ err, path });
             return ZxError.RedefinedRoute;
         };
@@ -124,22 +132,27 @@ pub const Zx = struct {
 
         if (response.request.method != .HEAD) {
             var ctz = ZxContext.init(allocator, response, body);
+            // Check to return html from lib/
             if (response.request.method == .GET and zx.has_html_route(response.request.target)) {
-                log.info("Returning html file at {s}...", .{response.request.target});
                 try zx.return_html(&ctz);
             } else {
-                var handler = zx.routes.get(response.request.target);
+                var handler = zx.routes.get(ctz.method_route);
                 if (handler) |h| {
                     h(&ctz) catch |err| {
-                        errorHandler(response, err);
+                        log.err("Caught error handling route {any}", .{err});
+                        errorHandler(response, .internal_server_error);
                     };
                 } else {
-                    std.debug.print("No route defined for {s}\n", .{response.request.target});
+                    log.warn("No route defined for {s}\n", .{response.request.target});
 
                     response.status = .not_found;
                     response.do() catch return ZxError.DoError;
                 }
             }
+            //
+            // if (response.state != .responded) {
+            //     errorHandler(response, );
+            // }
             response.finish() catch |err| {
                 log.err("Got finish error {any}\n", .{err});
                 return ZxError.FinishError;
@@ -151,17 +164,28 @@ pub const Zx = struct {
     }
 
     fn has_html_route(zx: *Zx, route_target: string) bool {
+        // for (zx.html_files_map.keys()) |key| {
+        //     log.info("html route at {s}", .{key});
+        // }
         return zx.html_files_map.contains(route_target);
     }
 
     fn return_html(zx: *Zx, ctz: *ZxContext) ZxError!void {
         var html = zx.html_files_map.get(ctz.response.request.target).?;
-        try ctz.html(html);
+        try ctz.html_dynamic(html, .ok);
     }
 
-    fn errorHandler(response: *http.Server.Response, err: ZxError) void {
-        log.err("Got error while handling route {any}\n", .{err});
-        response.status = .internal_server_error;
+    fn errorHandler(response: *http.Server.Response, status: ?http.Status) void {
+        if (status) |stat| {
+            response.status = stat;
+        } else {
+            response.status = .internal_server_error;
+        }
+        if (response.state == .waited) {
+            response.do() catch {
+                log.err("Already responded to client, cannot send error {any}", .{response.status});
+            };
+        }
     }
 };
 
@@ -169,45 +193,77 @@ pub const ZxContext = struct {
     allocator: std.mem.Allocator,
     response: *http.Server.Response,
     req_body: string,
+    method_route: MethodRoute,
 
     pub fn init(alloc: std.mem.Allocator, res: *http.Server.Response, body: string) ZxContext {
+        var method_route = MethodRoute.create(res.request.method, res.request.target);
         return ZxContext{
             .allocator = alloc,
             .response = res,
             .req_body = body,
+            .method_route = method_route,
         };
     }
 
-    /// Helper function to return JSON to the client
-    pub fn json(ctz: *ZxContext, content: anytype) ZxError!void {
+    /// Wrapper on ZxContext.respond() to return JSON to the client
+    pub fn json(ctz: *ZxContext, content: anytype, status: ?std.http.Status) ZxError!void {
         // Add json header
         ctz.response.headers.append("Content-Type", "application/json") catch return ZxError.Unexpected;
 
         const json_content = std.json.stringifyAlloc(ctz.allocator, content, .{}) catch return ZxError.OutOfMemory;
         defer ctz.allocator.free(json_content);
 
-        try ctz.respond(json_content);
+        try ctz.respond(json_content, status);
     }
 
     test "json helper function" {}
 
-    pub fn html(ctz: *ZxContext, html_content: string) ZxError!void {
+    /// Wrapper on ZxContext.respond() to return html
+    pub fn html_dynamic(ctz: *ZxContext, html_content: string, status: std.http.Status) ZxError!void {
+        var valid = HtmlValidator.validate(html_content);
+        if (!valid) {
+            log.err("Invalid html returned from route", .{});
+        }
+
         // Add html header
         ctz.response.headers.append("Content-Type", "text/html") catch return ZxError.Unexpected;
 
-        try ctz.respond(html_content);
+        try ctz.respond(html_content, status);
     }
 
-    // Helper function to return body to the client
-    pub fn respond(ctz: *ZxContext, body: string) ZxError!void {
+    /// Wrapper on ZxContext.respond() to return html, compile-time checked html
+    pub fn html(ctz: *ZxContext, comptime html_content: string, status: std.http.Status) ZxError!void {
+        comptime {
+            var valid = HtmlValidator.comp_validate(html_content);
+            if (!valid) {
+                @compileError("Trying to return invalid html\n" ++ html_content);
+            }
+        }
+        // Add html header
+        ctz.response.headers.append("Content-Type", "text/html") catch |err| {
+            log.err("{any} while appending headers to html response", .{err});
+            return ZxError.Unexpected;
+        };
+
+        try ctz.respond(html_content, status);
+    }
+
+    /// Helper function to return `body` and optional `status` to the client
+    pub fn respond(ctz: *ZxContext, body: string, status: ?std.http.Status) ZxError!void {
         if (!ctz.response.headers.contains("Content-Type")) {
             ctz.response.headers.append("Content-Type", "text/html") catch return ZxError.Unexpected;
         }
+        if (status) |stat| {
+            ctz.response.status = stat;
+        }
 
         ctz.response.transfer_encoding = .{ .content_length = body.len };
         // Send headers
         ctz.response.do() catch return ZxError.DoError;
         // Send html body
-        ctz.response.writeAll(body) catch return ZxError.Unexpected;
+        ctz.response.writeAll(body) catch |err| {
+            log.err("{any} while writing to response body", .{err});
+            return ZxError.Unexpected;
+        };
     }
 };