Skip to content

DXBE-166/CLI-1759: Report non-JSON API responses instead of a bare "Syntax error" - #2067

Closed
rfay wants to merge 2 commits into
acquia:mainfrom
rfay:20261021_rfay_non_json_api_errors
Closed

rfay wants to merge 2 commits into
acquia:mainfrom
rfay:20261021_rfay_non_json_api_errors

Conversation

@rfay

@rfay rfay commented Oct 21, 2026

Copy link
Copy Markdown

Motivation

During today's (2026-10-21) Cloud Platform API outage, https://cloud.acquia.com/api/* returned HTTP 503 with an HTML "Technical Difficulties / Temporarily Unavailable" page. Every acli command that calls the API, including auth:login, then failed with only this:

In Client.php line 167:
  Syntax error

followed by the advice to run acli self-update.

DDEV hit this in CI. DDEV's Acquia provider runs acli -n auth:login --key=... --secret=... before pull:db and pull:files, and its TestAcquiaPull integration test failed with the output above (failing job). The OAuth token request to accounts.acquia.com succeeded; the 503 came from the /api/account call that auth:login makes to look up the account email. Since 4.2.0 and 4.2.1 had just been released and the banner recommended updating, the failure first looked like an acli regression. Neither the message nor the exit status showed that the API was down.

The cause is in typhonius/acquia-php-sdk-v2: Client::processResponse() special-cases a 500 status, but sends every other response to json_decode(..., JSON_THROW_ON_ERROR). So any non-JSON response (an HTML error page from a 502, 503 or 504, or an empty body) surfaces as PHP's bare JsonException message.

Proposed changes

  • Add Acquia\Cli\CloudApi\CloudApiClient, which extends the SDK Client. Its processResponse() turns that JsonException into an AcquiaCliException naming the HTTP status, the reason phrase, and X-Request-Id when the response has one:

    In CloudApiClient.php line 34:
      The Acquia API returned HTTP 503 Service Unavailable with a response that is
      not JSON. The service may be temporarily unavailable; try again later.
      Request ID: v-43a1b606-cd98-11f1-a234-ff7814bf83ee
    
  • ClientService::getClient() builds a CloudApiClient. Its declared return type stays Client, so existing mocks and callers are unaffected.

  • AcsfClient decodes Site Factory responses the same way and had the same failure. It now extends CloudApiClient and raises the same error.

Successful responses and JSON error responses are handled exactly as before. The only change is that a non-JSON response now raises an AcquiaCliException instead of a JsonException, and nothing in src/ catches JsonException around a client call.

Alternatives considered

  • Fix it in typhonius/acquia-php-sdk-v2. That would also be worthwhile, but acli users would need a new SDK release to benefit, and AcsfClient would still need its own handling.
  • Check Content-Type or the status code before decoding. Catching the decode failure also covers a 200 response with an empty or truncated body, and it doesn't depend on which headers the gateway sends.

Testing steps

  1. Follow the contribution guide to set up your development environment or download a pre-built acli.phar for this PR.
  2. If running from source, clear the kernel cache to pick up new and changed commands: ./bin/acli ckc
  3. Check for regressions: composer test passes (lint, phpcs, PHPStan, full unit suite). Any normal authenticated command, such as acli api:accounts:find, still returns data.
  4. Check new functionality: vendor/bin/phpunit tests/phpunit/src/CloudApi/CloudApiClientTest.php covers a JSON response, an HTML 503 with a Request ID, and an empty body without one, for both clients. To see it against a real non-JSON response, point acli at a host that serves HTML: ACLI_CLOUD_API_BASE_URI=https://example.com ACLI_ACCESS_TOKEN=dummy ACLI_ACCESS_TOKEN_EXPIRY=9999999999 ./bin/acli -n api:accounts:find. On main this prints Syntax error; on this branch it prints The Acquia API returned HTTP 404 Not Found with a response that is not JSON.... During today's outage I ran the same command without the base URI override: main printed Syntax error, and this branch printed the 503 message above.

🤖 Generated with Claude Code

During a Cloud Platform API outage, cloud.acquia.com/api returns a 503
HTML page. The SDK's processResponse() only special-cases 500, so the
HTML reached json_decode(JSON_THROW_ON_ERROR) and every command,
including auth:login, failed with "In Client.php line 167: Syntax error".

Wrap the Cloud API client (and the ACSF client, which decodes the same
way) so a non-JSON response raises an AcquiaCliException naming the
HTTP status and the X-Request-Id, and saying the service may be
temporarily unavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 21, 2026 21:46
@github-actions

Copy link
Copy Markdown
Contributor

Try the dev build for this PR: https://acquia-cli.s3.amazonaws.com/build/pr/2067/acli.phar

curl -OL https://acquia-cli.s3.amazonaws.com/build/pr/2067/acli.phar
chmod +x acli.phar

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The narrowly scoped exception handling preserves existing client contracts and has focused regression coverage, with no blocking issues identified.

No open issues 🎉

What changed in this PR

Improves Acquia CLI diagnostics when Cloud Platform or Site Factory returns a non-JSON response, replacing bare JSON parsing errors with actionable HTTP details.

Changes:

  • Adds a shared exception message containing the HTTP status, reason phrase, and request ID when available.
  • Integrates the new handling into both API clients while preserving existing response processing.
  • Adds tests for valid JSON, HTML errors, empty responses, and client construction.
File Description
tests/​phpunit/​src/​CloudApi/​CloudApiClientTest.php Tests response handling and diagnostics for both clients.
tests/​phpunit/​src/​CloudApi/​ClientServiceTest.php Verifies construction of the new client.
src/​CloudApi/​CloudApiClient.php Converts JSON parsing failures into descriptive CLI exceptions.
src/​CloudApi/​ClientService.php Constructs CloudApiClient without changing the declared return type.
src/​AcsfApi/​AcsfClient.php Reuses the diagnostics for Site Factory responses.

Review effort: Balanced

@deepakmishra2 deepakmishra2 added the bug Something isn't working label Oct 22, 2026
@codecov

codecov Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.54%. Comparing base (fc68f17) to head (31027c5).

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #2067      +/-   ##
============================================
+ Coverage     93.51%   93.54%   +0.02%     
- Complexity     2252     2257       +5     
============================================
  Files           136      137       +1     
  Lines          7926     7942      +16     
============================================
+ Hits           7412     7429      +17     
+ Misses          514      513       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wimleers

wimleers commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Hah, ironically I ran into the same pattern a day earlier — but AFAICT the fix actually belongs upstream: typhonius/acquia-php-sdk-v2#545

I defer to acli maintainers — pinged @deepakmishra2 .

@wimleers wimleers changed the title Report non-JSON API responses instead of a bare "Syntax error" DXBE-166: Report non-JSON API responses instead of a bare "Syntax error" Oct 22, 2026
@wimleers wimleers changed the title DXBE-166: Report non-JSON API responses instead of a bare "Syntax error" DXBE-166/CLI-1759: Report non-JSON API responses instead of a bare "Syntax error" Oct 22, 2026
@deepakmishra2

Copy link
Copy Markdown
Contributor

Hah, ironically I ran into the same pattern a day earlier — but AFAICT the fix actually belongs upstream: typhonius/acquia-php-sdk-v2#545

I defer to acli maintainers — pinged @deepakmishra2 .

It makes more sense to get this done upstream and release and then bring it here. i think PR is already opened and i'm happy to approve

@wimleers

Copy link
Copy Markdown
Member

It makes more sense to get this done upstream and release and then bring it here. i think PR is already opened and i'm happy to approve

Great, thanks!

@wimleers

Copy link
Copy Markdown
Member

typhonius/acquia-php-sdk-v2#545 is in; closing this. The next acli release will include that updated dependency (it was released in https://github.com/typhonius/acquia-php-sdk-v2/releases/tag/3.9.0).

@wimleers wimleers closed this Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants