usestrix/strix · error · SpecParseError

Unexpected Postman API response shape for {label}

Error message

Unexpected Postman API response shape for {label}

What it means

SpecParseError raised when the Postman API returns valid JSON whose top level is not an object (e.g. a JSON array or bare string). The Postman REST API always wraps responses in an object, so this indicates a nonstandard intermediary or an API change.

Source

Thrown at strix/utils/api_spec.py:271

            timeout=_POSTMAN_FETCH_TIMEOUT,
        )
    except requests.RequestException as exc:
        raise SpecParseError(f"Failed to reach the Postman API: {exc}") from exc

    if response.status_code == 401:
        raise SpecParseError("Postman API rejected the key (401). Check POSTMAN_API_KEY.")
    if response.status_code == 404:
        raise SpecParseError(
            f"Postman {label} not found (404). Check the id and that the key can access it.",
        )
    if response.status_code != 200:
        raise SpecParseError(f"Postman API returned HTTP {response.status_code} for {label}.")
    try:
        payload = response.json()
    except ValueError as exc:
        raise SpecParseError(f"Postman API returned non-JSON for {label}") from exc
    if not isinstance(payload, dict):
        raise SpecParseError(f"Unexpected Postman API response shape for {label}")
    return payload


def fetch_postman_collection(collection_uid: str, api_key: str) -> dict[str, Any]:
    """Fetch a collection from the Postman API and return the raw collection dict.

    Uses ``GET /collections/{uid}`` with the ``X-Api-Key`` header. The endpoint
    wraps the collection under a ``collection`` key, unwrapped here so the result
    matches an exported collection file.
    """
    payload = _postman_api_json(
        f"{POSTMAN_API_BASE}/collections/{collection_uid}",
        api_key,
        f"collection {collection_uid}",
    )
    collection = payload.get("collection", payload)
    if not isinstance(collection, dict) or not collection:
        raise SpecParseError(f"Postman collection {collection_uid} came back empty")

View on GitHub (pinned to 8551339130)

Solutions

  1. Print the raw response to see its actual shape: add a temporary dump or curl the endpoint directly
  2. Fix the mock/fixture to mirror the real envelope ({"collection": {...}})
  3. If Postman itself changed shape, pin to a local export until the tool is updated

Example fix

# before (mock returns)
[{"item": []}]

# after (mock returns the real envelope)
{"collection": {"info": {"_postman_id": "..."}, "item": []}}
Defensive patterns

Strategy: type-guard

Validate before calling

payload = resp.json()
if not isinstance(payload, dict):
    raise ValueError(f"expected object envelope, got {type(payload).__name__}: {str(payload)[:80]}")

Type guard

def is_postman_envelope(p) -> bool:
    return isinstance(p, dict)

Try / catch

except SpecParseError as e:
    if "response shape" in str(e):
        fail loudly with the raw payload attached — this means a mock or gateway is broken, not a transient issue

Prevention

When it happens

Trigger: A mock/stub server standing in for the Postman API that returns a bare array; an API gateway stripping the envelope; Postman shipping a breaking format change.

Common situations: Local development against a mocked Postman API; HTTP interception tools returning canned fixtures.

Related errors


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/5cf796e8563e347a. Report an issue: GitHub.