fix(web-app-n8n): check the api-keys payload before walking it

The provisioning script normalised the listing into `existing` and then read
`key.get("label")` on every element without checking that the elements are
objects. Three shapes the endpoint can answer with pass that normalisation:
`data` holding an object, whose iteration yields its string keys; a bare JSON
string, whose iteration yields characters; and a list of strings. Each raises
`AttributeError: 'str' object has no attribute 'get'` from inside the
comprehension, naming neither the endpoint nor the payload.

The status guard above cannot catch it, because a non-JSON body only reaches
the caller as a string on HTTPError, and this answer was a 200. The task
retries thirty times over five minutes, so a shape that stays wrong consumes
the whole budget before failing without a diagnosis.

Report the shape and stop. Only the structure is printed, never the values,
because the payload is API data. A `null` body still passes: json.loads
returns None for it, which is a normal answer here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-10-08 00:48:40 +02:00
1 parent 59840c3362
commit 1c716e6a18
1 file changed
+8
@@ -118,6 +118,14 @@ def api_key():
sys.exit(f"FAILED listing api keys: {status} {body}")
existing = (body or {}).get("data") if isinstance(body, dict) else body
if existing and not (
isinstance(existing, list) and all(isinstance(key, dict) for key in existing)
):
sys.exit(
f"FAILED listing api keys: expected a list of key objects, got "
f"{type(existing).__name__} carrying "
f"{sorted(existing) if isinstance(existing, dict) else [type(k).__name__ for k in existing]}"
)
matches = [key for key in existing or [] if key.get("label") == KEY_NAME]
if len(matches) > 1:
sys.exit(f"FAILED: {len(matches)} api keys named {KEY_NAME}")