sansan0/TrendRadar · error · FileParseError

FILE_PARSE_ERROR

FILE_PARSE_ERROR

Error message

解析文件 {config_path} 失败: 配置文件不存在

What it means

Raised inside RSSParser._parse_json_feed when the content was detected as a JSON Feed (by _is_json_feed) but json.loads fails. Note the detection only sniffs JSON shape, so content that looks JSON-ish (starts like an object) yet contains syntax errors reaches this decoder and raises ValueError wrapping the JSONDecodeError with position info.

Solutions

  1. Read the wrapped JSONDecodeError position from the message and inspect content around that offset — 'Expecting value' at line 1 col 1 usually means BOM/HTML, mid-string errors mean truncation.
  2. Strip a leading BOM before parsing: content = content.lstrip('\ufeff').
  3. Re-fetch the feed and check Content-Length vs len(content) to detect truncation; retry on mismatch.
  4. Verify the URL actually serves a JSON Feed (version_url / top-level 'version' key) and that no HTML error page is being passed in.

Example fix

# before
data = json.loads(content)  # raises on BOM/truncated body

# after (defensive caller)
content = content.lstrip('\ufeff')
try:
    items = parser.parse(content, feed_url)
except ValueError as e:
    if len(content) < 200:
        logger.warning("feed %s returned suspiciously short body", feed_url)
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

def json_feed_probe(content: str) -> bool:
    """Verify JSON-decodability before handing to the parser."""
    try:
        json.loads(content.lstrip("\ufeff"))
        return True
    except json.JSONDecodeError:
        return False

Try / catch

try:
    items = parser.parse(content, feed_url)
except ValueError as e:
    if "JSON Feed" in str(e):
        logger.warning("feed %s sent malformed JSON; refetching once", feed_url)
        content = refetch(feed_url)
        items = parser.parse(content, feed_url)  # single retry, not a loop

Prevention

When it happens

Trigger: Calling parse() with content that _is_json_feed classifies as JSON but is malformed: truncated response body (cut mid-download), BOM prefixed to the body (\ufeff{...}) which json.loads rejects, single quotes / trailing commas from a hand-edited file, or content-type sniffing wrong on an HTML fragment that happens to start with '{'.

Common situations: Fetching a JSON feed over an unstable proxy that truncates the body; a server sending UTF-8-BOM; copying feed samples through tools that smart-quote characters; upstream switching from XML to JSON or vice versa so the sniffer picks the wrong branch on partially migrated endpoints.

Related errors


AI-assisted analysis of sansan0/TrendRadar@8ee26026ba (2026-08-15). Data as JSON: /api/errors/dc34d398fa89299e. Report an issue: GitHub.

Appendix: source

Thrown at mcp_server/services/parser_service.py:367

        """
        解析YAML配置文件

        Args:
            config_path: 配置文件路径,默认为 config/config.yaml

        Returns:
            配置字典

        Raises:
            FileParseError: 配置文件解析错误
        """
        if config_path is None:
            config_path = self.project_root / "config" / "config.yaml"
        else:
            config_path = Path(config_path)

        if not config_path.exists():
            raise FileParseError(str(config_path), "配置文件不存在")

        try:
            with open(config_path, "r", encoding="utf-8") as f:
                config_data = yaml.safe_load(f)
            return config_data
        except Exception as e:
            raise FileParseError(str(config_path), str(e))

    def parse_frequency_words(self, words_file: str = None) -> List[Dict]:
        """
        解析关键词配置文件(带 mtime 缓存)

        仅当 frequency_words.txt 被修改时才重新解析,避免循环内重复 IO。

        复用 trendradar.core.frequency 的解析逻辑,支持:
        - # 开头的注释行
        - 空行分隔词组
        - [组别名] 作为词组第一行,给整组指定别名

View on GitHub (pinned to 8ee26026ba)