-
Notifications
You must be signed in to change notification settings - Fork 5.2k
fix(source-github): make parse_response and error handlers defensive against unexpected response shapes #77685
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Patrick Nilan (pnilan)
merged 8 commits into
master
from
devin/1777672505-source-github-defensive-parse-response
May 3, 2026
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7380edc
fix(source-github): make parse_response and error handlers defensive …
devin-ai-integration[bot] d6f3201
fix: update changelog with PR number
devin-ai-integration[bot] 46ea5c3
fix: remove unused GithubStreamABCErrorHandler import
devin-ai-integration[bot] 7338b72
fix: address review feedback - guard body=None, yield base record on …
devin-ai-integration[bot] 02fc609
fix: reduce response text snippet to 50 chars, add warning logs in er…
devin-ai-integration[bot] fa60984
merge: update from main, resolve conflicts, bump to 2.1.27, fix broke…
devin-ai-integration[bot] 09e85a7
style: fix prettier formatting on test_config.json
devin-ai-integration[bot] 4074cc3
refactor: extract repeated defensive JSON parsing into _safe_json_lis…
devin-ai-integration[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| # Copyright (c) 2023 Airbyte, Inc., all rights reserved. | ||
| # | ||
|
|
||
| import logging | ||
| from typing import Optional, Union | ||
|
|
||
| import requests | ||
|
|
@@ -14,6 +15,9 @@ | |
| from . import constants | ||
|
|
||
|
|
||
| logger = logging.getLogger("airbyte") | ||
|
|
||
|
|
||
| GITHUB_DEFAULT_ERROR_MAPPING = DEFAULT_ERROR_MAPPING | { | ||
| 401: ErrorResolution( | ||
| response_action=ResponseAction.RETRY, | ||
|
|
@@ -54,7 +58,14 @@ | |
|
|
||
| def is_conflict_with_empty_repository(response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> bool: | ||
| if isinstance(response_or_exception, requests.Response) and response_or_exception.status_code == requests.codes.CONFLICT: | ||
| response_data = response_or_exception.json() | ||
| try: | ||
| response_data = response_or_exception.json() | ||
| except ValueError: | ||
| logger.warning( | ||
| "is_conflict_with_empty_repository received non-JSON 409 response (first 50 chars: %r).", | ||
| response_or_exception.text[:50], | ||
| ) | ||
| return False | ||
| return response_data.get("message") == "Git Repository is empty." | ||
| return False | ||
|
|
||
|
|
@@ -64,6 +75,10 @@ def is_gone_with_feature_disabled(response_or_exception: Optional[Union[requests | |
| try: | ||
| message = (response_or_exception.json().get("message") or "").lower() | ||
| except ValueError: | ||
| logger.warning( | ||
| "is_gone_with_feature_disabled received non-JSON 410 response (first 50 chars: %r).", | ||
| response_or_exception.text[:50], | ||
| ) | ||
| return False | ||
| return "are disabled" in message or "is disabled" in message | ||
| return False | ||
|
|
@@ -74,14 +89,26 @@ def __init__(self, stream: HttpStream, **kwargs): # type: ignore # noqa | |
| self.stream = stream | ||
| super().__init__(**kwargs) | ||
|
|
||
| def _safe_json_check_graphql_rate_limited(self, response: requests.Response) -> bool: | ||
| try: | ||
| body = response.json() | ||
| except ValueError: | ||
| self._logger.warning( | ||
| "GraphQL rate-limit check received non-JSON response (HTTP %s, first 50 chars: %r).", | ||
| response.status_code, | ||
| response.text[:50], | ||
| ) | ||
| return False | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. log warning
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — added a warning log with HTTP status and first 50 chars of the response body in 02fc609. |
||
| return self.stream.check_graphql_rate_limited(body or {}) | ||
|
|
||
| def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution: | ||
| if isinstance(response_or_exception, requests.Response): | ||
| retry_flag = ( | ||
| # The GitHub GraphQL API has limitations | ||
| # https://docs.github.com/en/graphql/overview/resource-limitations | ||
| ( | ||
| response_or_exception.headers.get("X-RateLimit-Resource") == "graphql" | ||
| and self.stream.check_graphql_rate_limited(response_or_exception.json()) | ||
| and self._safe_json_check_graphql_rate_limited(response_or_exception) | ||
| ) | ||
| # Rate limit HTTP headers | ||
| # https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limit-http-headers | ||
|
|
@@ -162,6 +189,13 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp | |
|
|
||
|
|
||
| class GitHubGraphQLErrorHandler(GithubStreamABCErrorHandler): | ||
| def _safe_json_get_errors(self, response: requests.Response) -> bool: | ||
| try: | ||
| body = response.json() | ||
| except ValueError: | ||
| return False | ||
| return bool((body or {}).get("errors")) | ||
|
|
||
| def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]] = None) -> ErrorResolution: | ||
| if isinstance(response_or_exception, requests.Response): | ||
| if response_or_exception.status_code in (requests.codes.BAD_GATEWAY, requests.codes.GATEWAY_TIMEOUT): | ||
|
|
@@ -176,7 +210,7 @@ def interpret_response(self, response_or_exception: Optional[Union[requests.Resp | |
| constants.DEFAULT_PAGE_SIZE_FOR_LARGE_STREAM if self.stream.large_stream else constants.DEFAULT_PAGE_SIZE | ||
| ) | ||
|
|
||
| if response_or_exception.json().get("errors"): | ||
| if self._safe_json_get_errors(response_or_exception): | ||
| return ErrorResolution( | ||
| response_action=ResponseAction.RETRY, | ||
| failure_type=FailureType.transient_error, | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log warning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done — added a warning log with the first 50 chars of the response body in 02fc609.