From 0bc9702854b6dc6e952012352375410277ca2368 Mon Sep 17 00:00:00 2001 From: Zed Date: Wed, 19 Aug 2026 06:28:53 +0700 Subject: [PATCH] Detect and handle Cloudflare error responses Fixes #1434 --- src/apiutils.nim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/apiutils.nim b/src/apiutils.nim index e6a63f0..b2aad58 100644 --- a/src/apiutils.nim +++ b/src/apiutils.nim @@ -11,6 +11,20 @@ const npCache = "x-np-cache" errorsToSkip = {null, doesntExist, tweetNotFound, timeout, unauthorized, badRequest} +proc isCloudflareHtml*(body: string): bool = + ## Detect Cloudflare HTML error pages returned instead of JSON + if body.len < 14 or body[0] != '<': return false + body[0 ..< 14].toLowerAscii() == " from Cloudflare HTML for log diagnostics + let start = body.find("") + if start < 0: return "unknown" + let contentStart = start + 7 + let stop = body.find("", contentStart) + if stop < 0: return "unknown" + body[contentStart ..< stop].splitWhitespace().join(" ") + var pool: HttpPool disableTid: bool @@ -151,6 +165,10 @@ template fetchImpl(result, fetchBody) {.dirty.} = if resp.headers.getOrDefault("content-encoding") == "gzip": result = uncompress(result, dfGzip) + if isCloudflareHtml(result): + echo "[cloudflare] ", resp.status, " (", cfTitle(result), "), API: ", url.path, ", session: ", session.pretty + raise rateLimitError() + if result.startsWith("{\"errors"): let errors = result.fromJson(Errors) if errors notin errorsToSkip: