From 7063ca9f681fe00673528ffa6f13ff71cec3abe2 Mon Sep 17 00:00:00 2001 From: justrach <54503978+justrach@users.noreply.github.com> Date: Sat, 26 Sep 2026 17:37:03 +0800 Subject: [PATCH] fix(semantic): classify non-standard 5xx provider responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A signed synthetic hosted request returned HTTP 530. The previous enum-only status switch treated that server-side failure as a client rejection, so explicit index builds skipped their bounded retry budget and diagnostics blamed the client. Classify the numeric 500–599 range as provider unavailable while preserving 429 and 4xx handling; add HTTP-mock coverage for 403 and 530. This does not restore the hosted service or alter ranking or retrieval breadth. Co-Authored-By: Codegraff --- src/semantic.zig | 11 ++++++----- src/test_semantic.zig | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/semantic.zig b/src/semantic.zig index ac1c63b6..12d75977 100644 --- a/src/semantic.zig +++ b/src/semantic.zig @@ -344,11 +344,12 @@ fn fetchHttpResponseOnce( .response_writer = &response_writer, }); if (fetch_result.status != .ok) { - return switch (fetch_result.status) { - .too_many_requests => error.EmbeddingRateLimited, - .internal_server_error, .bad_gateway, .service_unavailable, .gateway_timeout => error.EmbeddingProviderUnavailable, - else => error.EmbeddingProviderRejected, - }; + if (fetch_result.status == .too_many_requests) return error.EmbeddingRateLimited; + // Gateways can emit non-standard 5xx codes that the status enum does + // not name. They are still provider failures, not client rejections. + const status_code = @intFromEnum(fetch_result.status); + if (status_code >= 500 and status_code < 600) return error.EmbeddingProviderUnavailable; + return error.EmbeddingProviderRejected; } return try allocator.dupe(u8, response_writer.buffer[0..response_writer.end]); } diff --git a/src/test_semantic.zig b/src/test_semantic.zig index 041195b2..c2098cfd 100644 --- a/src/test_semantic.zig +++ b/src/test_semantic.zig @@ -516,8 +516,10 @@ test "semantic: provider 4xx and 5xx responses fail closed with retryable classe defer guards.deinit(); for ([_]struct { status: []const u8, expected: anyerror }{ + .{ .status = "403 Forbidden", .expected = error.EmbeddingProviderRejected }, .{ .status = "429 Too Many Requests", .expected = error.EmbeddingRateLimited }, .{ .status = "500 Internal Server Error", .expected = error.EmbeddingProviderUnavailable }, + .{ .status = "530 Unknown Server Error", .expected = error.EmbeddingProviderUnavailable }, }) |case| { const addr = try std.Io.net.IpAddress.parse("127.0.0.1", 0); var server = try addr.listen(io, .{ .reuse_address = true, .mode = .stream, .protocol = .tcp });