{"record":{"id":"b00109c0e33c5e4f","repo":"symfony/thanks","slug":"errors","errorCode":null,"errorMessage":"errors","messagePattern":"errors","errorType":"exception","errorClass":"TransportException","httpStatus":null,"severity":"error","filePath":"src/GitHubClient.php","lineNumber":180,"sourceCode":"        $options = [\n            'http' => [\n                'method' => 'POST',\n                'content' => json_encode(['query' => $graphql]),\n                'header' => ['Content-Type: application/json'],\n            ],\n        ];\n\n        if ($this->rfs instanceof HttpDownloader) {\n            $result = $this->rfs->get('https://api.github.com/graphql', $options)->getBody();\n        } else {\n            $result = $this->rfs->getContents('github.com', 'https://api.github.com/graphql', false, $options);\n        }\n\n        $result = json_decode($result, true);\n\n        if (isset($result['errors'][0]['message'])) {\n            if (!isset($result['data'])) {\n                throw new TransportException($result['errors'][0]['message']);\n            }\n\n            foreach ($result['errors'] as $error) {\n                if (!isset($error['path'])) {\n                    $failures[isset($error['type']) ? $error['type'] : $error['message']] = $error['message'];\n                    continue;\n                }\n\n                foreach ($error['path'] as $path) {\n                    $failures += [$path => $error['message']];\n                    unset($result['data'][$path]);\n                }\n            }\n        }\n\n        return isset($result['data']) ? $result['data'] : [];\n    }\n","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/symfony/thanks/blob/f455cc9ba4e0c61dcc18ea7e52bd725ee661aa64/src/GitHubClient.php#L162-L198","documentation":"The GitHub GraphQL API responded with one or more top-level errors and no partial 'data' payload, so call() throws a TransportException whose message is the first GraphQL error message returned by GitHub. This means the whole query failed server-side rather than individual fields being null; per-field errors with a 'path' would instead be recorded in $failures and stripped from the data. The library treats this as a transport-level abort because nothing usable was returned.","triggerScenarios":"Any call() to https://api.github.com/graphql (via getRepositories -> processChunks) where the JSON response contains result['errors'][0]['message'] but no result['data']: malformed GraphQL query, authentication failure (bad/expired GitHub token), rate limiting, or GitHub returning a top-level error object.","commonSituations":"Running composer-funding lookups with an unset or revoked GITHUB_TOKEN, hitting the GraphQL rate limit after many chunked queries, GitHub API 5xx responses serialized as an errors payload, or a library/version mismatch producing an invalid query shape.","solutions":["Check your GitHub token is set and valid (GITHUB_TOKEN or COMPOSER_GITHUB_TOKEN env vars) and re-authenticate if expired","Retry later if rate-limited; GraphQL rate limits reset hourly — reduce query volume or raise the limit by authenticating","Verify network/proxy access to api.github.com and retry on transient 5xx failures","Update the composer plugin/library to the latest version in case the GraphQL query is outdated"],"exampleFix":"// before: run with no token\n$ composer outdated --direct   # -> TransportException: Bad credentials\n// after\n$ export GITHUB_TOKEN=ghp_xxx   # or configure github.oauth in COMPOSER_HOME/auth.json\n$ composer outdated --direct","handlingStrategy":"try-catch","validationCode":"if (!getenv('GITHUB_TOKEN') && !getenv('COMPOSER_GITHUB_TOKEN')) {\n    fwrite(STDERR, \"No GitHub token configured; GraphQL calls may fail with 'Bad credentials'.\\n\");\n}","typeGuard":"function hasUsableGraphqlPayload(?array $result): bool\n{\n    return $result !== null\n        && isset($result['data'])\n        && !isset($result['errors'][0]['message']) === false;\n}","tryCatchPattern":"try {\n    $repos = $client->getRepositories($urls);\n} catch (Composer\\Downloader\\TransportException $e) {\n    $msg = $e->getMessage();\n    if (str_contains($msg, 'API rate limit exceeded')) {\n        // wait for rate-limit reset, then retry\n    } elseif (str_contains($msg, 'Bad credentials')) {\n        // prompt user to refresh their GitHub token\n    } else {\n        throw $e;\n    }\n}","preventionTips":["Always configure a GitHub token (GITHUB_TOKEN env var or auth.json) before running queries","Keep queries chunked (as processChunks does) to stay under GraphQL complexity/rate limits","Check X-RateLimit-Remaining headers / GH rate-limit API before large batches","Handle partial results via the $failures array instead of treating any error as fatal"],"tags":["graphql","github-api","transport-exception","authentication","rate-limit"],"backgroundTag":"api-error-response","analyzedSha":"f455cc9ba4e0c61dcc18ea7e52bd725ee661aa64","analyzedAt":"2026-09-13T17:43:01.449Z","contentChangedAt":"2026-09-13T17:43:01.449Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}