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

  1. 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.
  2. If the status is 429/5xx with HTML, back off and retry the request later; nothing in your payload is wrong.
  3. 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.
  4. 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

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


AI-assisted analysis of Anankke/SSPanel-UIM@d55a607191 (2026-08-21). Data as JSON: /api/errors/1a40277ab69ca6f5. Report an issue: GitHub.