nexu-io/open-design · error · SystemExit

image API response did not contain data[0].b64_json

Error message

image API response did not contain data[0].b64_json

What it means

Raised by decode_response when response["data"][0] exists but is not a dict, or its b64_json field is missing/not a string. The script hard-requires a base64 PNG payload at data[0].b64_json and cannot fall back to a URL or any other field.

Source

Thrown at skills/hatch-pet/scripts/generate_pet_images.py:158

        data=payload,
        headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=300) as resp:
        output_json.write_bytes(resp.read())
    response = json.loads(output_json.read_text(encoding="utf-8"))
    if response.get("error"):
        raise SystemExit(json.dumps(response["error"], indent=2))
    return response


def decode_response(response: dict[str, object], output_image: Path) -> None:
    data = response.get("data")
    if not isinstance(data, list) or not data:
        raise SystemExit("image API response did not contain data[0]")
    first = data[0]
    if not isinstance(first, dict) or not isinstance(first.get("b64_json"), str):
        raise SystemExit("image API response did not contain data[0].b64_json")
    output_image.parent.mkdir(parents=True, exist_ok=True)
    output_image.write_bytes(base64.b64decode(first["b64_json"]))


def file_sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as file:
        for chunk in iter(lambda: file.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def complete_job(job: dict[str, object], output_path: Path) -> None:
    job["status"] = "complete"
    job["source_path"] = str(output_path)
    job["source_provenance"] = "secondary-fallback-image-api"
    job["source_sha256"] = file_sha256(output_path)
    job["output_sha256"] = file_sha256(output_path)

View on GitHub (pinned to 5be4028344)

Solutions

  1. Inspect run_dir/raw/<job_id>.response.json to confirm whether data[0] carries url vs b64_json.
  2. Ensure the request asks for base64: the generations payload already sets output_format png, but if the model needs response_format=b64_json, add it to the payload/fields.
  3. Switch to a model that returns b64_json for the requested size.
  4. If only URLs are available, extend decode_response to download data[0]["url"] into output_image.

Example fix

// before
first = data[0]
if not isinstance(first, dict) or not isinstance(first.get("b64_json"), str):
    raise SystemExit("image API response did not contain data[0].b64_json")

// after - fall back to url when b64_json is absent
first = data[0]
if not isinstance(first, dict):
    raise SystemExit("image API response data[0] is not an object")
if isinstance(first.get("b64_json"), str):
    output_image.write_bytes(base64.b64decode(first["b64_json"]))
elif isinstance(first.get("url"), str):
    with urllib.request.urlopen(first["url"], timeout=300) as remote:
        output_image.write_bytes(remote.read())
else:
    raise SystemExit("image API response did not contain data[0].b64_json or url")
Defensive patterns

Strategy: type-guard

Validate before calling

def extract_image_bytes(first_entry: object) -> bytes:
    if not isinstance(first_entry, dict):
        raise SystemExit("data[0] is not an object")
    b64 = first_entry.get("b64_json")
    if isinstance(b64, str):
        return base64.b64decode(b64)
    url = first_entry.get("url")
    if isinstance(url, str):
        with urllib.request.urlopen(url, timeout=300) as remote:
            return remote.read()
    raise SystemExit("data[0] has neither b64_json nor url")

Type guard

def has_b64(entry: object) -> bool:
    return isinstance(entry, dict) and isinstance(entry.get("b64_json"), str)

Prevention

When it happens

Trigger: The images/generations or images/edits call succeeded but returned data[0] = {"url": "..."} (url format) instead of b64_json, or returned data[0] as a non-dict element, or b64_json was emitted as null/empty by the model.

Common situations: Default response_format for the model is url and the script did not request b64_json; using a third-party OpenAI-compatible image API that only returns URLs; a model variant that omits b64_json on certain sizes.

Related errors


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