From 81278de25070157f2b5f28e58f629d37655336c1 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Sat, 26 Sep 2026 07:52:19 +0800 Subject: [PATCH 1/2] Don't skip navigation when page isn't loaded (or had error loading) --- src/browser/tools.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/browser/tools.zig b/src/browser/tools.zig index d70f19860..c1b0b7372 100644 --- a/src/browser/tools.zig +++ b/src/browser/tools.zig @@ -2373,7 +2373,10 @@ fn renderJson(arena: std.mem.Allocator, value: anytype) ToolError![]const u8 { fn ensurePage(session: *lp.Session, registry: *NodeRegistry, url: ?[:0]const u8, timeout: ?u32) ToolError!*lp.Frame { if (url) |u| { if (session.currentFrame()) |frame| { - if (std.mem.eql(u8, frame.url, u)) return frame; + const is_loaded = frame._parse_state != .pre and frame._last_navigate_error == null; + if (is_loaded and std.mem.eql(u8, frame.url, u)) { + return frame; + } } _ = try performGoto(session, registry, u, .{ .timeout = timeout }); } From b9d8b39c683c658fe8f4015c3fac542f1ab696e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sat, 26 Sep 2026 21:36:11 +0200 Subject: [PATCH 2/2] Test that a same-url read re-navigates after a failed navigation The failed frame's url already matches, so markdown/tree with that url used to return the error placeholder page instead of navigating again. --- src/browser/tools.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/browser/tools.zig b/src/browser/tools.zig index c1b0b7372..4dc1bdceb 100644 --- a/src/browser/tools.zig +++ b/src/browser/tools.zig @@ -2997,3 +2997,18 @@ test "isPathSafe: absolute paths and traversal are rejected" { try std.testing.expect(!isPathSafe("sub/..")); try std.testing.expect(!isPathSafe("..")); } + +test "markdown: a same-url page whose navigation failed is navigated again" { + var registry: NodeRegistry = .init(std.testing.allocator); + defer registry.deinit(); + + const session = testing.test_session; + defer if (session.primaryPage()) |page| page.close(); + + const aa = testing.arena_allocator; + const args = try std.json.parseFromSliceLeaky(std.json.Value, aa, + \\{"url":"http://localhost:1/"} + , .{}); + try std.testing.expect((try call(aa, session, ®istry, "goto", args, .{})).is_error); + try std.testing.expect((try call(aa, session, ®istry, "markdown", args, .{})).is_error); +}