koala73/worldmonitor · error · Error
Cloudflare ${method} ${path} did not complete (a write may s
Error message
Cloudflare ${method} ${path} did not complete (a write may still have landed): ${error.message} What it means
cloudflareRequest wraps fetch calls to the Cloudflare API. When a request times out (AbortSignal.timeout) or fails at the transport level, it rethrows with the method, path, and original error. The message deliberately warns that for a write, the operation may still have landed server-side despite the client not seeing a response, so the operator must re-read state before retrying.
Solutions
- Before retrying a write, GET the target ruleset/rule to check whether the write already landed.
- Increase timeoutMs or fix network connectivity if timeouts are frequent.
- Retry idempotent reads freely; for writes, make the retry idempotent (same body) only after confirming current state.
- Check Cloudflare API status if many requests time out at once.
Example fix
// before
await cloudflareRequest(path, { method: 'PATCH', body, timeoutMs: 2000 });
// after
try {
await cloudflareRequest(path, { method: 'PATCH', body, timeoutMs: 10000 });
} catch (e) {
const current = await cloudflareRequest(readPath, { method: 'GET' });
if (!matchesDesiredState(current)) throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight connectivity probe before writes
const probe = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', { signal: AbortSignal.timeout(5000) });
if (!probe.ok) throw new Error('Cloudflare API unreachable; skipping write'); Try / catch
try {
await cloudflareRequest(path, { token, method: 'PATCH', body, fetchImpl });
} catch (e) {
if (e.message.includes('did not complete (a write may still have landed)')) {
const current = await cloudflareRequest(readPath, { token, fetchImpl }); // re-read before retry
if (!stateMatchesIntent(current)) throw e;
return; // write had landed
}
throw e;
} Prevention
- Set generous but bounded timeoutMs for write operations.
- Always re-read state after an ambiguous write failure before retrying.
- Make write retries idempotent (same body produces same desired state).
- Monitor Cloudflare API status pages during incidents.
When it happens
Trigger: The fetch to /zones/.../rulesets/... times out via AbortSignal.timeout(timeoutMs), the connection is reset, DNS fails, or TLS/transport errors occur mid-request.
Common situations: Slow or flaky network to api.cloudflare.com, an overly tight timeoutMs, corporate proxy interference, or a Cloudflare API incident causing long latency on a PATCH/POST write.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- DNS ${recordType} lookup failed: HTTP ${response.status}
- callbackUrl DNS resolution failed: ${message}
- Cloudflare Radar API error: ${resp.status}
- bodyTransportFailure ? 'NHC_POINT_REQUEST_FAILED' : 'NHC_POINT_RESPONSE_INVALID'
- Convex embed key validation unavailable: fetch-error
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/8d0de6d25051a698.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloudflare-cache-rule.mjs:502
) {
// A hung API call must not park an `--apply` between the read and the write
// forever; fail loudly instead so the operator can retry against a fresh read.
let response;
try {
response = await fetchImpl(`${CLOUDFLARE_API}${path}`, {
method,
headers: {
Authorization: `Bearer ${token}`,
'User-Agent': USER_AGENT,
...(body ? { 'Content-Type': 'application/json' } : {}),
},
...(body ? { body: JSON.stringify(body) } : {}),
signal: AbortSignal.timeout(timeoutMs),
});
} catch (error) {
// A timeout or transport failure on a write is ambiguous — the write may
// still have landed — so name the request the operator has to re-read for.
throw new Error(`Cloudflare ${method} ${path} did not complete (a write may still have landed): ${error.message}`);
}
const payload = await response.json().catch(() => null);
if (!response.ok || !payload?.success) {
const detail = JSON.stringify(payload?.errors ?? payload ?? response.statusText);
throw new Error(`Cloudflare ${method} ${path} failed (${response.status}): ${detail}`);
}
return payload.result;
}
export async function resolveZoneId(token, { env = process.env, fetchImpl } = {}) {
if (env.CLOUDFLARE_ZONE_ID) {
// Never take the id on trust. The credential that actually runs this locally
// is account-wide, so a stale or mistyped id would aim every write at another
// zone's cache rules — and the script would report success.
const zone = await cloudflareRequest(`/zones/${encodeURIComponent(env.CLOUDFLARE_ZONE_ID)}`, {
token,
fetchImpl,
});View on GitHub (pinned to 7d06c8633d)