Tencent/WeKnora · error

parse feed %s: %w

Error message

parse feed %s: %w

What it means

Returned by Connector.Validate when the feed bytes were fetched successfully but gofeed's parser.Parse could not recognize them as RSS, Atom, or JSON Feed. gofeed sniffs the document structure; a 200 response that is an HTML page (login page, soft-404, bot-challenge, or homepage) fails parsing even though the fetch succeeded.

Source

Thrown at internal/datasource/connector/rss/connector.go:46

func (c *Connector) Type() string { return types.ConnectorTypeRSS }

// Validate verifies that every configured feed URL is reachable and parses as
// a valid feed.
func (c *Connector) Validate(ctx context.Context, config *types.DataSourceConfig) error {
	cfg, err := parseConfig(config)
	if err != nil {
		return err
	}
	cli := newClient(cfg.parseHeaders())
	parser := gofeed.NewParser()

	for _, feedURL := range cfg.feedURLList() {
		data, err := cli.fetchFeed(ctx, feedURL)
		if err != nil {
			return fmt.Errorf("fetch feed %s: %w", feedURL, err)
		}
		if _, err := parser.Parse(bytes.NewReader(data)); err != nil {
			return fmt.Errorf("parse feed %s: %w", feedURL, err)
		}
	}
	return nil
}

// ResolveResourceAncestors has nothing to do: feeds are a flat list with no
// nesting, so a selection has no ancestors to reveal.
func (c *Connector) ResolveResourceAncestors(
	ctx context.Context, config *types.DataSourceConfig, resourceIDs []string,
) ([]string, error) {
	return []string{}, nil
}

// ListResources returns one resource per configured feed URL. The feed is
// fetched so the resource can carry its real title; a feed that fails to fetch
// still appears (named by URL) with an error note, so the user can deselect it
// instead of the whole listing failing.
func (c *Connector) ListResources(

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Confirm the URL returns actual RSS/Atom: open it in a browser or validate the response with an XML parser; use the site's real feed path (commonly /feed, /rss.xml, /atom.xml).
  2. Validate the feed XML with xmllint or the W3C feed validator to find the exact malformation, then fix it at the source.
  3. If a bot-protection page is served with status 200, request a rule exemption for the connector's User-Agent or fetch via a cache/proxy.
  4. Check you are not pointing at sitemap.xml or an HTML page — gofeed only parses RSS, Atom, and JSON Feed.

Example fix

// before
feeds: ["https://blog.example"]
// after
feeds: ["https://blog.example/feed.xml"]
Defensive patterns

Strategy: validation

Validate before calling

func sniffIsFeed(body []byte) bool {
    s := strings.TrimSpace(string(body[:min(len(body), 512)]))
    lower := strings.ToLower(s)
    return strings.HasPrefix(lower, "<?xml") &&
        (strings.Contains(lower, "<rss") || strings.Contains(lower, "<feed")) ||
        strings.HasPrefix(lower, "{") && strings.Contains(lower, "\"version\"")
}

Try / catch

err := connector.Validate(ctx, cfg)
if err != nil && strings.Contains(err.Error(), "parse feed") {
    feedURL := extractFeedURL(err.Error())
    return fmt.Errorf("%s did not return RSS/Atom/JSON feed content; check the URL points at the feed, not the site", feedURL)
}

Prevention

When it happens

Trigger: The URL returns HTML instead of a feed (soft-404s served with status 200, bot-protection interstitials, a homepage URL pasted instead of the feed URL); the feed is malformed XML with encoding errors or truncated tags; the document is a format gofeed doesn't support (e.g. RDF with unusual extensions, or a sitemap.xml mistaken for a feed).

Common situations: Users paste the site homepage rather than /feed or /rss.xml; feeds behind Cloudflare serving a challenge page with status 200; hand-edited or generator-broken feeds with invalid XML; WordPress pretty-permalink changes breaking the feed URL.

Related errors


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