OpenBMB/ChatDev · error · FileNotFoundError

Attachment source not found: {source}

Error message

Attachment source not found: {source}

What it means

AttachmentManager.register_file raises plain FileNotFoundError when the local path passed in does not exist on disk — checked before any MIME guessing, hashing, or persistence. It is a pre-registration sanity check, not a download failure.

Source

Thrown at utils/attachments.py:89

    def register_file(
        self,
        file_path: Path | str,
        *,
        kind: MessageBlockType = MessageBlockType.FILE,
        display_name: Optional[str] = None,
        mime_type: Optional[str] = None,
        attachment_id: Optional[str] = None,
        copy_file: bool = True,
        description: Optional[str] = None,
        extra: Optional[Dict[str, Any]] = None,
        persist: bool = True,
        deduplicate: bool = False,
    ) -> AttachmentRecord:
        """Register a local file and return its attachment record."""
        source = Path(file_path)
        if not source.exists():
            raise FileNotFoundError(f"Attachment source not found: {source}")

        guessed_mime = mime_type or (mimetypes.guess_type(source.name)[0] or "application/octet-stream")
        attachment_id = attachment_id or uuid.uuid4().hex
        sha256_source = _sha256_file(source)

        if deduplicate:
            existing = self._find_duplicate_by_hash(
                sha256_source,
                copy_file=copy_file,
                source_path=source,
            )
            if existing:
                return existing
        if copy_file:
            target_dir = self.root / attachment_id
            target_dir.mkdir(parents=True, exist_ok=True)
            target_path = target_dir / source.name
            shutil.copy2(source, target_path)

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Verify the path exists (and is absolute) before calling register_file
  2. Check the upstream producer of the file actually wrote it (earlier step may have failed silently)
  3. Use Path(...).resolve() to normalize relative paths against an explicit base

Example fix

# before
rec = manager.register_file(p)
# after
p = Path(p).resolve()
if not p.is_file():
    raise FileNotFoundError(f"expected export at {p}")
rec = manager.register_file(p)
Defensive patterns

Strategy: type-guard

Validate before calling

p = Path(file_path).resolve()
if not p.is_file():
    raise FileNotFoundError(f'missing attachment source: {p}')

Type guard

def is_registerable_file(p: str | Path) -> bool:
    return Path(p).is_file()

Try / catch

try:
    rec = manager.register_file(p)
except FileNotFoundError:
    p = re_generate_export(); rec = manager.register_file(p)

Prevention

When it happens

Trigger: Calling register_file (directly or via load_file/save_upload_file/_persist_single_attachment/report_export_pdf) with a path whose file was moved, deleted, or never written; relative path resolved against an unexpected CWD; typo in the path.

Common situations: Temp file cleaned up before registration; path built from user input that was never validated; running the process from a different working directory so relative paths break; race where an upstream step failed to produce the file.

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 OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27). Data as JSON: /api/errors/e120bfbe37ea6cf9. Report an issue: GitHub.