DenverCoder1/github-readme-streak-stats · error · AssertionError

500

500

Error message

You don't have a valid SSL Certificate installed or XAMPP.

What it means

executeContributionGraphRequests() performs cURL requests to the GitHub GraphQL API. cURL error 60 (SSL certificate problem) is caught specifically and rethrown as AssertionError telling the developer their environment lacks a valid SSL CA certificate setup, commonly an XAMPP/local-dev issue.

Solutions

  1. Download cacert.pem and set curl.cainfo and openssl.cafile in php.ini to its path
  2. Update the operating system's CA certificate bundle (e.g. apt-get install --reinstall ca-certificates)
  3. Restart the web server/PHP-FPM after changing php.ini
  4. Check for TLS-intercepting proxies and add their root CA to the bundle

Example fix

// before (php.ini)
;curl.cainfo =
// after
curl.cainfo = "C:\xampp\php\extras\ssl\cacert.pem"
openssl.cafile = "C:\xampp\php\extras\ssl\cacert.pem"
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check: can PHP reach GitHub over TLS?
$r = curl_init("https://api.github.com"); curl_setopt($r, CURLOPT_NOBODY, true); curl_setopt($r, CURLOPT_RETURNTRANSFER, true); curl_exec($r); if (curl_errno($r) === 60) { die("Fix CA bundle in php.ini"); }

Try / catch

try { $graphs = getContributionGraphs($user); } catch (AssertionError $e) { if (str_contains($e->getMessage(), "SSL Certificate")) { log_ssl_config_error(); } throw $e; }

Prevention

When it happens

Trigger: A cURL request to the GitHub API fails with error code 60 (CURLE_PEER_FAILED_VERIFICATION): missing or outdated CA bundle, self-signed proxies, or XAMPP's default certificate configuration.

Common situations: Local development with XAMPP where php.ini has no curl.cainfo set; corporate proxy with TLS interception; outdated CA certificate bundle on the server.

Understand the failure class

Related errors


AI-assisted analysis of DenverCoder1/github-readme-streak-stats@70dd50f921 (2026-09-15). Data as JSON: /api/errors/bc6984b1bfe8f9a0. Report an issue: GitHub.

Appendix: source

Thrown at src/stats.php:74

        curl_multi_add_handle($multi, $handle);
    }
    // execute queries
    $running = null;
    do {
        curl_multi_exec($multi, $running);
    } while ($running);
    // collect responses
    $responses = [];
    foreach ($requests as $year => $handle) {
        $contents = curl_multi_getcontent($handle);
        $decoded = is_string($contents) ? json_decode($contents) : null;
        // if response is empty or invalid, retry request one time or throw an error
        if (empty($decoded) || empty($decoded->data) || !empty($decoded->errors)) {
            $message = $decoded->errors[0]->message ?? ($decoded->message ?? "An API error occurred.");
            $error_type = $decoded->errors[0]->type ?? "";
            // Missing SSL certificate
            if (curl_errno($handle) === 60) {
                throw new AssertionError("You don't have a valid SSL Certificate installed or XAMPP.", 500);
            }
            // Other cURL error
            elseif (curl_errno($handle)) {
                throw new AssertionError("cURL error: " . curl_error($handle), 500);
            }
            // GitHub API error - Not Found
            elseif ($error_type === "NOT_FOUND") {
                throw new InvalidArgumentException("Could not find a user with that name.", 404);
            }
            // if rate limit is exceeded, don't retry with same token
            if (str_contains($message, "rate limit exceeded")) {
                removeGitHubToken($tokens[$year]);
            }
            error_log("First attempt to decode response for $user's $year contributions failed. $message");
            error_log("Contents: $contents");
            // retry request
            $query = buildContributionGraphQuery($user, $year);
            $token = getGitHubToken();

View on GitHub (pinned to 70dd50f921)