{"record":{"id":"9dfef34b741b3d7d","repo":"Tencent/WeKnora","slug":"parse-feed-s-w","errorCode":null,"errorMessage":"parse feed %s: %w","messagePattern":"parse feed (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/rss/connector.go","lineNumber":46,"sourceCode":"func (c *Connector) Type() string { return types.ConnectorTypeRSS }\n\n// Validate verifies that every configured feed URL is reachable and parses as\n// a valid feed.\nfunc (c *Connector) Validate(ctx context.Context, config *types.DataSourceConfig) error {\n\tcfg, err := parseConfig(config)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcli := newClient(cfg.parseHeaders())\n\tparser := gofeed.NewParser()\n\n\tfor _, feedURL := range cfg.feedURLList() {\n\t\tdata, err := cli.fetchFeed(ctx, feedURL)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"fetch feed %s: %w\", feedURL, err)\n\t\t}\n\t\tif _, err := parser.Parse(bytes.NewReader(data)); err != nil {\n\t\t\treturn fmt.Errorf(\"parse feed %s: %w\", feedURL, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// ResolveResourceAncestors has nothing to do: feeds are a flat list with no\n// nesting, so a selection has no ancestors to reveal.\nfunc (c *Connector) ResolveResourceAncestors(\n\tctx context.Context, config *types.DataSourceConfig, resourceIDs []string,\n) ([]string, error) {\n\treturn []string{}, nil\n}\n\n// ListResources returns one resource per configured feed URL. The feed is\n// fetched so the resource can carry its real title; a feed that fails to fetch\n// still appears (named by URL) with an error note, so the user can deselect it\n// instead of the whole listing failing.\nfunc (c *Connector) ListResources(","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/rss/connector.go#L28-L64","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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).","Validate the feed XML with xmllint or the W3C feed validator to find the exact malformation, then fix it at the source.","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.","Check you are not pointing at sitemap.xml or an HTML page — gofeed only parses RSS, Atom, and JSON Feed."],"exampleFix":"// before\nfeeds: [\"https://blog.example\"]\n// after\nfeeds: [\"https://blog.example/feed.xml\"]","handlingStrategy":"validation","validationCode":"func sniffIsFeed(body []byte) bool {\n    s := strings.TrimSpace(string(body[:min(len(body), 512)]))\n    lower := strings.ToLower(s)\n    return strings.HasPrefix(lower, \"<?xml\") &&\n        (strings.Contains(lower, \"<rss\") || strings.Contains(lower, \"<feed\")) ||\n        strings.HasPrefix(lower, \"{\") && strings.Contains(lower, \"\\\"version\\\"\")\n}","typeGuard":null,"tryCatchPattern":"err := connector.Validate(ctx, cfg)\nif err != nil && strings.Contains(err.Error(), \"parse feed\") {\n    feedURL := extractFeedURL(err.Error())\n    return fmt.Errorf(\"%s did not return RSS/Atom/JSON feed content; check the URL points at the feed, not the site\", feedURL)\n}","preventionTips":["Verify the URL serves RSS/Atom XML (or JSON Feed) before configuring it — prefer /feed, /rss.xml, /atom.xml over the site root.","Run feeds through the W3C feed validator or xmllint when a generator is suspected.","Watch for bot-protection pages served with status 200; exempt the connector's User-Agent if needed.","Keep feed URLs from trusted sources; hand-edited feeds often have malformed XML."],"tags":["xml","parsing","rss","validation"],"backgroundTag":"feed-parse-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}