NanmiCoder/MediaCrawler · warning · NoteNotFoundError

Note not found or abnormal, code: {data['code']}

Error message

Note not found or abnormal, code: {data['code']}

What it means

NoteNotFoundError raised by XiaoHongShuClient.request when the response JSON's code equals -510000 (note not found) or -510001 (note abnormal/deleted). The endpoint answered successfully but reported the requested note is gone, so retrying the same note_id will not help.

Source

Thrown at media_platform/xhs/client.py:193

            str(response_data.get("code"))
            if response_data is not None and response_data.get("code") is not None
            else ""
        )
        if response_code == str(self.IP_ERROR_CODE):
            raise IPBlockError(self.IP_ERROR_STR)
        if response_code == str(self.SECURITY_LIMIT_CODE):
            raise PlatformAccessError(
                f"XHS account security restriction, code: {self.SECURITY_LIMIT_CODE}"
            )

        if return_response:
            return response.text
        data: Dict = response_data if response_data is not None else response.json()
        if data["success"]:
            return data.get("data", data.get("success", {}))
        # IP_ERROR_CODE / SECURITY_LIMIT_CODE are already handled above, before return_response.
        elif data["code"] in (self.NOTE_NOT_FOUND_CODE, self.NOTE_ABNORMAL_CODE):
            raise NoteNotFoundError(f"Note not found or abnormal, code: {data['code']}")
        else:
            err_msg = data.get("msg", None) or f"{response.text}"
            raise DataFetchError(err_msg)

    @staticmethod
    def _build_query_string(params: Dict) -> str:
        """Build URL query string with encoding matching browser behavior (commas not encoded)"""
        parts = []
        for key, value in params.items():
            value_str = str(value) if value is not None else ""
            parts.append(f"{key}={quote(value_str, safe=',')}")
        return "&".join(parts)

    async def get(self, uri: str, params: Optional[Dict] = None) -> Dict:
        """
        GET request, signs request headers
        Args:
            uri: Request route

View on GitHub (pinned to d6f7c5bb90)

Solutions

  1. Treat as terminal for that note: catch NoteNotFoundError, log, and continue the crawl.
  2. If it happens for every note, suspect an expired or wrong xsec_token being passed and re-fetch search results to get fresh tokens.
  3. Filter note IDs by publish recency to avoid fetching already-removed content.
  4. Do not retry the same note_id - these codes are deterministic.

Example fix

// before
note = await xhs_client.get_note_by_id(note_id)
// after
from media_platform.xhs.exception import NoteNotFoundError
try:
    note = await xhs_client.get_note_by_id(note_id)
except NoteNotFoundError:
    utils.logger.info(f"note {note_id} gone, skipping")
    note = None
Defensive patterns

Strategy: try-catch

Try / catch

from media_platform.xhs.exception import NoteNotFoundError
try:
    note = await xhs_client.get_note_by_id(note_id)
except NoteNotFoundError:
    utils.logger.info(f"note {note_id} deleted/abnormal - skipped")
    note = None

Prevention

When it happens

Trigger: Calling get_note_by_id (or comment APIs) with a note_id that was deleted, set to private, or removed by moderation after it was discovered by search/creator crawling.

Common situations: Long crawls where search results contain notes deleted before their detail fetch; note links shared from other users whose authors later deleted them; xsec_token mismatch causing the API to treat the note as inaccessible.

Related errors


AI-assisted analysis of NanmiCoder/MediaCrawler@d6f7c5bb90 (2026-08-15). Data as JSON: /api/errors/2538c4407a05ac0f. Report an issue: GitHub.