Anankke/SSPanel-UIM · error · App\Services\Gateway\Cryptomus\RequestBuilderException
json_last_error_msg()
Error message
json_last_error_msg()
What it means
This exception is thrown when the Cryptomus endpoint returned a non-empty body that is not valid JSON — json_decode($response, true) produced null. The message is PHP's json_last_error_msg() (typically "Syntax error"), and the exception code is the real HTTP status, so a 502/503 HTML page from a proxy in front of the API surfaces here. It can also false-positive on a body that is literally the JSON literal "null" (json_decode('null') === null) even though that is technically valid JSON.
Source
Thrown at src/Services/Gateway/Cryptomus/RequestBuilder.php:63
CURLOPT_URL => $url,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => 1,
],
);
$response = curl_exec($curl);
$responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($response === false) {
throw new RequestBuilderException(curl_error($curl), $responseCode, $uri);
}
if ($response !== '') {
$json = json_decode($response, true);
if ($json === null) {
throw new RequestBuilderException(json_last_error_msg(), $responseCode, $uri);
}
if ($responseCode !== 200 || ($json['state'] !== null && $json['state'] !== 0)) {
if (isset($json['message']) && $json['message'] !== '') {
throw new RequestBuilderException($json['message'], $responseCode, $uri);
}
if (isset($json['errors']) && $json['errors'] !== []) {
throw new RequestBuilderException('Validation error', $responseCode, $uri, $json['errors']);
}
}
if (isset($json['result']) && $json['state'] !== null && $json['state'] === 0) {
return $json['result'];
}
}
return true;View on GitHub (pinned to d55a607191)
Solutions
- Log the raw $response body (not just json_last_error_msg()) alongside the HTTP code — the HTML title of an error page immediately reveals whether the failure is Cloudflare/WAF, an origin 502, or truncation.
- If the status is 429/5xx with HTML, back off and retry the request later; nothing in your payload is wrong.
- If a genuine decode error repeats with status 200, capture the exact body bytes and report it to Cryptomus support / check for proxies stripping content.
- Harden the check: distinguish 'null' from invalid JSON by comparing json_last_error() against JSON_ERROR_NONE instead of relying on the null return.
Example fix
// before
$json = json_decode($response, true);
if ($json === null) {
throw new RequestBuilderException(json_last_error_msg(), $responseCode, $uri);
}
// after
$json = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new RequestBuilderException(
json_last_error_msg() . ': ' . substr($response, 0, 200),
$responseCode,
$uri
);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
$result = $rb->sendRequest('v1/payment', $data);
} catch (App\Services\Gateway\Cryptomus\RequestBuilderException $e) {
if (in_array($e->getCode(), [502, 503, 429], true) && str_contains($e->getMessage(), 'Syntax error')) {
// gateway/CDN returned a non-JSON page — transient, retry after backoff
return retryWithBackoff(fn () => $rb->sendRequest('v1/payment', $data), 3);
}
if (str_starts_with($e->getMessage(), 'Syntax error') || $e->getMessage() === 'Malformed UTF-8 characters') {
// persistent bad payload — log code + message and surface for investigation
$log->error('Cryptomus non-JSON response', ['status' => $e->getCode(), 'method' => $e->getMethod()]);
}
throw $e;
} Prevention
- Log the HTTP status together with this exception — a 502/503 HTML page needs different handling than a 200 with a broken body.
- Treat 'Syntax error' + 5xx as transient and retry; treat 'Syntax error' + 200 as a data/integration bug worth reporting.
- When extending RequestBuilder, include a truncated raw body in the exception message so future occurrences are diagnosable from logs.
When it happens
Trigger: api.cryptomus.com (or a CDN/proxy like Cloudflare in front of it) returns an HTML error page (502 Bad Gateway, 429 challenge/block page) instead of JSON; a rate-limit or WAF interceptor returns plain text ("Too many requests"); the response is valid JSON encoding the scalar null, e.g. the literal string "null", which json_decode maps to null and is misreported as a decode error; truncated body caused by a proxy cutting the connection mid-transfer.
Common situations: Shared-hosting IP being rate-limited or challenged by the gateway's CDN; transient upstream outage of Cryptomus returning HTML maintenance pages; hosting provider's transparent proxy mangling responses; scripts on old PHP versions where malformed UTF-8 bodies trigger "Malformed UTF-8 characters".
Related errors
- curl_error($curl)
- $msg_response->getBody()->getContents()
- $response->getBody()->getContents()
- $msg_response->getBody()->getContents()
AI-assisted analysis of Anankke/SSPanel-UIM@d55a607191 (2026-08-21).
Data as JSON: /api/errors/1a40277ab69ca6f5.
Report an issue: GitHub.