Skip to content

Action fails after "React API Cutover": empty job ID → polls /api/v1/jobs/ → "Response is not valid JSON" until timeout #18

Description

@pierzchala-m

Summary

After the recent commit 0d8f90d ("React API Cutover ahead of Opensource PR Merge"), mieweb/launchpad@main fails for a previously-working pull_request preview deploy. The container is reported "created" but with an empty job ID, after which Phase 2 polls GET /api/v1/jobs/ (no id), receives a non-JSON body (HTTP 200), logs Response is not valid JSON — retrying 30 times, and then fails with Timed out waiting for job to complete after 300s.

Consuming workflow: mieweb/FHIRTogether PR #51 preview deploy.

Observed logs (from the action, secrets redacted)

Querying API: ***/api/v1/sites/1/containers?hostname=mieweb-fhirtogether-feat-systemd-deployment
--- RAW API RESPONSE ---
{"data":[]}
------------------------
Creating container...
--- Create Payload ---
{ "hostname": "mieweb-fhirtogether-feat-systemd-deployment", "template": "ghcr.io/mieweb/fhirtogether:pr-51", "services": [ ... ] }
----------------------
Container created successfully (ID: , Job ID: ).      <-- both empty
...
Attempt 1/30 — HTTP 200
    Response is not valid JSON — retrying.
...
Attempt 30/30 — HTTP 200
    Response is not valid JSON — retrying.
::error::Timed out waiting for job to complete after 300s.

Root cause (response-schema drift)

The backend's JSON schema changed in the React cutover, but action.yml's parsing wasn't fully updated to match:

  1. List endpoint now returns {"data":[ ... ]}, but the action reads .containers[0].id:

    GET /api/v1/sites/$SITE_ID/containers?hostname=...   ->  {"data":[]}
    CONTAINER_ID=$(echo "$EXISTING_CONTAINER" | jq -r '.containers[0].id // empty')
    

    .containers no longer exists, so this is always empty.

  2. Create response no longer exposes .container.id / .id / .jobId where the action looks:

    NEW_ID=$(echo "$CREATE_BODY" | jq -r '.container.id // .id // empty')
    JOB_ID=$(echo "$CREATE_BODY" | jq -r '.jobId // empty')
    

    Result: Container created successfully (ID: , Job ID: ). — both empty.

  3. Empty JOB_ID then makes Phase 2 poll GET /api/v1/jobs/ (trailing-empty id). With no job id, the API returns the SPA HTML page (HTTP 200), so jq empty rejects it as non-JSON on every one of the 30 attempts, then the step times out.

So the failure isn't a transient outage — it's a consistent schema mismatch introduced by the API cutover while @main is consumed mid-migration.

Suggested fixes

  • Update action.yml parsing to the new schema (e.g. read the list from .data[...] and the create response/job id from their new locations), or
  • Have the API keep returning the legacy keys (containers, container.id, jobId) until the action is updated, and
  • Defensive: if JOB_ID is empty after create, fail fast with a clear error instead of polling /api/v1/jobs/ (which returns HTML and wastes 5 minutes). Optionally validate that the jobs endpoint returns application/json before parsing.

Environment

  • Action ref: mieweb/launchpad@main (failing commit ≈ 0d8f90d)
  • Trigger: pull_request (opened/synchronize)
  • Endpoints in use: /api/v1/sites/1/containers, /api/v1/jobs/<id>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions