nexu-io/open-design · error · SystemExit

Configured uploadPath for participant {participant['name']}

Error message

Configured uploadPath for participant {participant['name']} does not exist: {source}. Fix the upload avatar path before preparing the bundle.

What it means

Thrown by copy_avatar_assets() in prepare_chat_overlay_bundle.py when a participant's resolved uploadPath does not point at an existing file on disk. The script copies upload avatars into the bundle's public/ folder, so a missing source cannot be bundled. It fires during prepare, after the template has been copied but before chatSpec.ts is written; on failure the partial bundle is deleted.

Source

Thrown at skills/chat-motion-overlay/scripts/prepare_chat_overlay_bundle.py:96

def write_chat_spec_ts(spec: dict, output_dir: Path) -> None:
    target = output_dir / "src" / "chatSpec.ts"
    content = "export const chatSpec = " + json.dumps(spec, ensure_ascii=False, indent=2) + " as const;\n"
    target.write_text(content, encoding="utf-8")


def remove_local_upload_paths(spec: dict) -> None:
    for participant in spec["participants"]:
        participant.pop("uploadPath", None)


def copy_avatar_assets(spec: dict, output_dir: Path) -> dict:
    upload_sources: list[tuple[dict, Path]] = []
    for participant in spec["participants"]:
        upload_path = participant.get("uploadPath")
        if upload_path:
            source = Path(upload_path).expanduser().resolve()
            if not source.exists():
                raise SystemExit(
                    f"Configured uploadPath for participant {participant['name']} does not exist: {source}. "
                    "Fix the upload avatar path before preparing the bundle."
                )
            upload_sources.append((participant, source))
    public_dir = output_dir / "public"
    public_dir.mkdir(parents=True, exist_ok=True)
    for preset_file in avatar_library_dir().glob("*.png"):
        shutil.copy2(preset_file, public_dir / preset_file.name)
    for participant, source in upload_sources:
        target_name = f"{participant['id']}-upload{source.suffix.lower()}"
        shutil.copy2(source, public_dir / target_name)
        participant["uploadAsset"] = target_name
    remove_local_upload_paths(spec)
    return spec


def main() -> None:
    args = parse_args()

View on GitHub (pinned to 5be4028344)

Solutions

  1. Verify the uploadPath exists with `ls -l <path>` from the same cwd the script runs in.
  2. Use an absolute path (or a path starting with ~) for uploadPath to avoid cwd ambiguity; the script runs Path(...).expanduser().resolve().
  3. If the avatar is gone, regenerate or pick a preset avatar and switch avatarMode accordingly.

Example fix

// before
{"participants": {"老婆": {"uploadPath": "avatars/me.png"}}}  // not found from skill cwd
// after
{"participants": {"老婆": {"uploadPath": "~/projects/avatars/me.png"}}}
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
def assert_uploads_exist(spec: dict) -> None:
    missing = []
    for p in spec.get("participants", []):
        up = p.get("uploadPath")
        if up and not Path(up).expanduser().resolve().exists():
            missing.append((p["name"], up))
    if missing:
        raise SystemExit(f"upload avatars missing: {missing}")

Prevention

When it happens

Trigger: Run prepare_chat_overlay_bundle.py with avatarMode in {upload, mixed} and a participant uploadPath that does not exist (wrong path, typo, relative path resolved against the wrong cwd, or file deleted). The run_test_matrix.py case 'invalid_upload_missing_file' reproduces this.

Common situations: Relative uploadPath interpreted against a different cwd than expected; a path copied from another machine; the avatar file was moved or deleted after the config was written; ~ expansion differences.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/d402fbd7505f2feb. Report an issue: GitHub.