Tencent/WeKnora · error

HTTP %d %s

Error message

HTTP %d %s

What it means

Returned when the feed or article server responds with a non-2xx HTTP status. The error message carries the numeric status code and Go's canonical status text (e.g. "HTTP 404 Not Found"). No body is read or parsed in this case, so any error page content is discarded.

Source

Thrown at internal/datasource/connector/rss/client.go:84

			req.Header.Set(k, v)
		}
	}
	if req.Header.Get("User-Agent") == "" {
		req.Header.Set("User-Agent", defaultUserAgent)
	}
	if req.Header.Get("Accept") == "" {
		req.Header.Set("Accept",
			"application/rss+xml, application/atom+xml, application/xml, text/xml, application/json, text/html;q=0.9, */*;q=0.8")
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("fetch failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("HTTP %d %s", resp.StatusCode, resp.Status)
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, maxSize))
	if err != nil {
		return nil, fmt.Errorf("read body failed: %w", err)
	}
	return body, nil
}

// fetchFeed retrieves the raw bytes of a feed document.
func (c *client) fetchFeed(ctx context.Context, feedURL string) ([]byte, error) {
	return c.fetch(ctx, feedURL, maxFeedSize, true)
}

// extractArticle fetches an article page and returns the readability-cleaned
// main content as HTML, plus the extracted title (may be empty). Returns an
// error if the page can't be fetched or no readable content is found, so the
// caller can fall back to feed-provided content.

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Read the embedded status code: for 404 verify the feed URL still exists and update the config; for 401/403 check the configured auth headers in the datasource config.
  2. For 429, back off and retry later; reduce polling frequency.
  3. For 5xx, retry with backoff — the error is usually transient on the feed host's side.
  4. For 403 on article fetches, note that auth headers are intentionally withheld from third-party pages; if the article needs cookies, that content cannot be fetched by design.

Example fix

// before
if err := connector.Validate(ctx, cfg); err != nil {
    return err
}
// after
if err := connector.Validate(ctx, cfg); err != nil {
    var se *statusError // or strings.Contains on "HTTP 4"
    if errors.As(err, &se) && se.Code >= 500 {
        return retryLater(err)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Try / catch

data, err := cli.fetchFeed(ctx, feedURL)
if err != nil {
    msg := err.Error()
    switch {
    case strings.Contains(msg, "HTTP 429"):
        return backoffRetryAfterRateLimit(msg)
    case strings.Contains(msg, "HTTP 404"):
        return markFeedDead(feedURL)
    case strings.Contains(msg, "HTTP 5"):
        return backoffRetry(msg)
    default:
        return err
    }
}

Prevention

When it happens

Trigger: The remote server returned 4xx or 5xx for a GET on the feed or article URL: 404 after a feed moved, 401/403 when auth headers are missing/invalid (feed fetches attach configured headers; article fetches deliberately do not), 429 rate limiting, or 5xx server errors.

Common situations: Blog migrated and removed the old feed URL (404); feed requires a subscription token that was rotated (401); Cloudflare/WAF blocking the default User-Agent (403); aggressive per-IP rate limits on polling (429); transient upstream errors (502/503).

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/c0e7613f0708b9e8. Report an issue: GitHub.