jackwener/OpenCLI · error · CommandExecutionError
medium tag returned HTTP ${resp.status}
Error message
medium tag returned HTTP ${resp.status} What it means
After the request succeeds and is not a 404, any other non-OK HTTP status (403 rate-limit/bot-block, 5xx server errors, redirects mishandled) triggers CommandExecutionError 'medium tag returned HTTP <status>'. It reports the raw status so the caller can diagnose server-side or anti-bot rejections.
Source
Thrown at clis/medium/tag.js:113
try {
resp = await fetch(url, {
headers: {
'user-agent': 'opencli-medium-adapter (+https://github.com/jackwener/opencli)',
accept: 'application/rss+xml, application/xml',
},
});
}
catch (err) {
throw new CommandExecutionError(
`medium tag request failed: ${err?.message ?? err}`,
'Check that medium.com is reachable from this network.',
);
}
if (resp.status === 404) {
throw new EmptyResultError('medium tag', `Medium tag "${tag}" does not exist.`);
}
if (!resp.ok) {
throw new CommandExecutionError(`medium tag returned HTTP ${resp.status}`);
}
const xml = await resp.text();
const items = [];
const re = /<item[^>]*>([\s\S]*?)<\/item>/g;
let m;
while ((m = re.exec(xml)) !== null) {
items.push(m[1]);
}
if (!items.length) {
throw new EmptyResultError('medium tag', `Medium tag "${tag}" RSS feed has no items.`);
}
return items.slice(0, limit).map((block, i) => ({
rank: i + 1,
title: decodeHtml(extractTag(block, 'title')).trim(),
author: decodeHtml(extractTag(block, 'dc:creator')).trim(),
description: stripHtml(extractTag(block, 'description')),
categories: extractCategories(block).join(', '),
published: isoDateFromRfc822(decodeHtml(extractTag(block, 'pubDate')).trim()),View on GitHub (pinned to 49907e53dc)
Solutions
- Read the status in the message: 403/429 usually means bot mitigation or rate limiting — back off and retry with delays.
- Retry 5xx statuses after a short wait; they are typically transient.
- Try from a different network/IP (residential) if 403 persists.
- Check Medium's status page during apparent outages.
Example fix
// before (tight loop triggers 429)
for (const t of tags) await mediumTag(t);
// after
for (const t of tags) {
await mediumTag(t);
await new Promise(r => setTimeout(r, 2000));
} Defensive patterns
Strategy: retry
Validate before calling
// optional preflight of the endpoint's health
const res = await fetch('https://medium.com/feed/tag/programming');
if (!res.ok && res.status !== 404) throw new Error(`Medium unhealthy: HTTP ${res.status}`); Try / catch
try {
await run(['medium', 'tag', tag]);
} catch (e) {
const m = /returned HTTP (\d+)/.exec(e.message);
if (m && (m[1].startsWith('5') || m[1] === '429')) {
await backoffRetry(() => run(['medium', 'tag', tag]), 3);
} else throw e;
} Prevention
- Throttle requests to the feed endpoint; add delays between batch calls.
- Retry only transient statuses (429, 5xx) with exponential backoff.
- Avoid running from datacenter IPs that trip bot mitigation.
- Alert on sustained 403/429s rather than hot-looping retries.
When it happens
Trigger: Calling `medium tag <tag>` when Medium responds 403 (Cloudflare/bot mitigation), 429 (rate limited after rapid repeated calls), or 500/502/503 (Medium server issues).
Common situations: Hammering the feed endpoint in a loop from CI; requests from datacenter IPs flagged by bot protection; Medium incidents producing 5xx responses.
Related errors
- linux.do request failed: HTTP ${result.status ?? 'unknown'}
- medium tag request failed: ${err?.message ?? err}
- Xiaoyuzhou transcript download failed with HTTP ${response.s
- archive search failed: HTTP ${resp.status}
- autohome ${contextHint} HTTP ${resp.status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/619ef481a4249f5a.
Report an issue: GitHub.