calesthio/OpenMontage · error · HTTPException

invalid project id

Error message

invalid project id

What it means

The reference image decodes fine but its width or height falls outside the 300–6000 pixel window that Ark accepts for reference images. Both dimensions must independently be within [300, 6000]; tiny thumbnails and oversized panos are both rejected.

Source

Thrown at backlot/server.py:314

    # loaded, and browsers heuristically cache /ui assets. no-cache forces a
    # conditional revalidation (cheap 304 via ETag) on every load so UI fixes
    # show up on a plain refresh. Media/thumb responses keep normal caching.
    @app.middleware("http")
    async def ui_no_cache(request, call_next):
        response = await call_next(request)
        path = request.url.path
        if path == "/" or path.startswith("/ui") or path.startswith("/p/"):
            response.headers["Cache-Control"] = "no-cache"
        return response

    return app


def _safe_project_dir(project_id: str) -> Path:
    # ':' rejects Windows drive-relative ids like "C:" (PROJECTS_DIR / "C:"
    # collapses back to PROJECTS_DIR itself).
    if any(c in project_id for c in "/\\:") or project_id in (".", ".."):
        raise HTTPException(status_code=400, detail="invalid project id")
    project_dir = PROJECTS_DIR / project_id
    if not project_dir.is_dir():
        raise HTTPException(status_code=404, detail=f"unknown project: {project_id}")
    return project_dir


def _sse(payload: dict) -> str:
    return f"data: {json.dumps(payload)}\n\n"


def _thumbnail_for(source: Path, width: int) -> Optional[Path]:
    """Downscale an image (or extract a video poster frame) to a cached JPEG."""
    suffix = source.suffix.lower()
    is_image = suffix in {".png", ".jpg", ".jpeg", ".webp", ".gif"}
    is_video = suffix in {".mp4", ".webm", ".mov"}
    if not (is_image or is_video):
        return None
    try:

View on GitHub (pinned to 95e1c3d0ab)

Solutions

  1. Upscale small images or pick a larger source (>=300px each side).
  2. Downscale large images so the longest side is <=6000px (e.g. PIL thumbnail).
  3. Pick a source image already in a normal photo range (roughly 512–4096px) to satisfy both this and the ratio check.

Example fix

# before
inputs = {"reference_image_path": "tiny_128.png"}
# after
from PIL import Image
img = Image.open("tiny_128.png")
img = img.resize((max(512, img.width), max(512, img.height)))
img.save("ref_ok.png")
inputs = {"reference_image_path": "ref_ok.png"}
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image
with Image.open(ref_path) as im:
    w, h = im.size
assert 300 <= w <= 6000 and 300 <= h <= 6000, (w, h)

Type guard

def size_ok(w: int, h: int) -> bool:
    return 300 <= w <= 6000 and 300 <= h <= 6000

Try / catch

try:
    tool.run(inputs)
except ValueError as e:
    if "300 to 6000" in str(e):
        resize_into_range(ref_path)  # thumbnail() or upscale as needed, retry
        tool.run(inputs)
    else:
        raise

Prevention

When it happens

Trigger: A 128x128 icon/thumbnail as a character reference; a 8000x4000 stitched panorama; a 250px-wide cropped detail.

Common situations: Using favicon-scale or emoji-scale images as identity references; feeding print-resolution scans without downsampling; automatic thumbnail exports from a CMS.

Related errors


AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15). Data as JSON: /api/errors/7bf22617c053fdb9. Report an issue: GitHub.