HKUDS/Vibe-Trading · error · FileNotFoundError

Media file not found: {media_path}

Error message

Media file not found: {media_path}

What it means

_send_media_file verifies the media path with Path.is_file() before upload and raises FileNotFoundError if it does not exist. The media pipeline (AES-encrypt + CDN upload) requires the raw bytes, so a missing file fails fast before any network calls.

Source

Thrown at agent/src/channels/weixin.py:1342

    async def _send_media_file(
        self,
        to_user_id: str,
        media_path: str,
        context_token: str,
    ) -> None:
        """Upload a local file to WeChat CDN and send it as a media message.

        Follows the exact protocol from ``@tencent-weixin/openclaw-weixin`` v1.0.3:
        1. Generate a random 16-byte AES key (client-side).
        2. Call ``getuploadurl`` with file metadata + hex-encoded AES key.
        3. AES-128-ECB encrypt the file and POST to CDN (``{cdnBaseUrl}/upload``).
        4. Read ``x-encrypted-param`` header from CDN response as the download param.
        5. Send a ``sendmessage`` with the appropriate media item referencing the upload.
        """
        p = Path(media_path)
        if not p.is_file():
            raise FileNotFoundError(f"Media file not found: {media_path}")

        raw_data = p.read_bytes()
        raw_size = len(raw_data)
        raw_md5 = hashlib.md5(raw_data).hexdigest()

        # Determine upload media type from extension
        ext = p.suffix.lower()
        if ext in _IMAGE_EXTS:
            upload_type = UPLOAD_MEDIA_IMAGE
            item_type = ITEM_IMAGE
            item_key = "image_item"
        elif ext in _VIDEO_EXTS:
            upload_type = UPLOAD_MEDIA_VIDEO
            item_type = ITEM_VIDEO
            item_key = "video_item"
        elif ext in _VOICE_EXTS:
            upload_type = UPLOAD_MEDIA_VOICE
            item_type = ITEM_VOICE

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Verify the file exists (and capture its bytes) before queuing the send.
  2. Generate media into a stable directory you control and pass an absolute path.
  3. Keep the temp file alive until the send completes (close only after awaiting send).
  4. Check mounts/permissions if running in a container.

Example fix

// before
await channel.send_media(chat_id, media_path=tmp_path)

# after
p = Path(tmp_path).resolve()
if not p.is_file():
    raise FileNotFoundError(tmp_path)
await channel.send_media(chat_id, str(p))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def media_ok(path: str) -> bool:
    p = Path(path)
    return p.is_file() and p.stat().st_size > 0

Type guard

from pathlib import Path

def is_sendable_media(path: str) -> bool:
    p = Path(path)
    return p.is_file() and not p.is_dir()

Try / catch

try:
    await channel.send_media(chat_id, path)
except FileNotFoundError as e:
    if "Media file not found" in str(e):
        regenerate_or_skip(path)
    else:
        raise

Prevention

When it happens

Trigger: Calling send() with a media attachment whose media_path does not exist or is a directory — e.g. temp file already cleaned up, wrong path, file deleted between generation and send.

Common situations: Tempfile garbage collection (cleanup handlers, NamedTemporaryFile context exit) deleting the media before send; agent-generated artifacts written to a different working directory; path built with a stale filename; container filesystem where the mount is missing.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/2fbacdccac410d21. Report an issue: GitHub.