Repository navigation
Conversation
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>
|
Try the dev build for this PR: https://acquia-cli.s3.amazonaws.com/build/pr/2067/acli.phar |
There was a problem hiding this comment.
🟢 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
Codecov Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
|
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 |
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! |
|
typhonius/acquia-php-sdk-v2#545 is in; closing this. The next |
Motivation
During today's (2026-10-21) Cloud Platform API outage,
https://cloud.acquia.com/api/*returnedHTTP 503with an HTML "Technical Difficulties / Temporarily Unavailable" page. Every acli command that calls the API, includingauth:login, then failed with only this: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=...beforepull:dbandpull:files, and itsTestAcquiaPullintegration test failed with the output above (failing job). The OAuth token request to accounts.acquia.com succeeded; the 503 came from the/api/accountcall thatauth:loginmakes 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 tojson_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 bareJsonExceptionmessage.Proposed changes
Add
Acquia\Cli\CloudApi\CloudApiClient, which extends the SDKClient. ItsprocessResponse()turns thatJsonExceptioninto anAcquiaCliExceptionnaming the HTTP status, the reason phrase, andX-Request-Idwhen the response has one:ClientService::getClient()builds aCloudApiClient. Its declared return type staysClient, so existing mocks and callers are unaffected.AcsfClientdecodes Site Factory responses the same way and had the same failure. It now extendsCloudApiClientand 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
AcquiaCliExceptioninstead of aJsonException, and nothing insrc/catchesJsonExceptionaround a client call.Alternatives considered
typhonius/acquia-php-sdk-v2. That would also be worthwhile, but acli users would need a new SDK release to benefit, andAcsfClientwould still need its own handling.Content-Typeor 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
./bin/acli ckccomposer testpasses (lint, phpcs, PHPStan, full unit suite). Any normal authenticated command, such asacli api:accounts:find, still returns data.vendor/bin/phpunit tests/phpunit/src/CloudApi/CloudApiClientTest.phpcovers 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. Onmainthis printsSyntax error; on this branch it printsThe 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:mainprintedSyntax error, and this branch printed the 503 message above.🤖 Generated with Claude Code