From 01ddd25f835375e7e011ee1d7cb3463e1377f526 Mon Sep 17 00:00:00 2001 From: Adrian Edwards Date: Sat, 20 Jun 2026 13:38:55 -0400 Subject: [PATCH 1/5] update keyman key returning check to ensure both that the key exists AND that its value is not None Signed-off-by: Adrian Edwards --- keyman/KeyClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyman/KeyClient.py b/keyman/KeyClient.py index b950229fe..ad1a14fdc 100644 --- a/keyman/KeyClient.py +++ b/keyman/KeyClient.py @@ -104,7 +104,7 @@ def request(self, platform: str | None = None) -> str: self._send("NEW", key_platform = platform or self.platform) try: msg = self._recv() - if "key" in msg: + if msg.get("key") is not None: return msg["key"] raise Exception(f"Invalid response type: {msg}") except WaitKeyTimeout as e: From 6fee5367d3df084d897e4643832152444bf52a81 Mon Sep 17 00:00:00 2001 From: Adrian Edwards Date: Sun, 21 Jun 2026 13:04:26 -0400 Subject: [PATCH 2/5] have clients wait 5 minutes when requesting a new key when none are available Signed-off-by: Adrian Edwards --- keyman/Orchestrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyman/Orchestrator.py b/keyman/Orchestrator.py index 71cfae8bb..54ca064cd 100644 --- a/keyman/Orchestrator.py +++ b/keyman/Orchestrator.py @@ -156,7 +156,7 @@ def new_key(self, platform: str) -> str | None: if not len(self.fresh_keys[platform]): if not len(self.expired_keys[platform]): self.logger.warning(f"Key was requested for {platform}, but none are published") - return + raise WaitKeyTimeout(300) min_timeout = 0 for _, timeout in self.expired_keys[platform].items(): From be20ceef5998ac2248cbd44fbd2bb03d2dba8a14 Mon Sep 17 00:00:00 2001 From: Adrian Edwards Date: Mon, 22 Jun 2026 13:51:14 -0400 Subject: [PATCH 3/5] Check for an actual "bad credentials" message from github before raising an exception that will invalidate the key This should help interrupt the facade starvation pipeline. Signed-off-by: Adrian Edwards --- collectoss/tasks/github/util/github_data_access.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/collectoss/tasks/github/util/github_data_access.py b/collectoss/tasks/github/util/github_data_access.py index 6a603c3e3..12dbfe3c9 100644 --- a/collectoss/tasks/github/util/github_data_access.py +++ b/collectoss/tasks/github/util/github_data_access.py @@ -169,7 +169,14 @@ def make_request(self, url, method="GET", timeout=100): raise UrlNotFoundException(f"Could not find {url}") if response.status_code == 401: - raise NotAuthorizedException(f"Could not authorize with the github api using key: {mask_key(self.key)}") + resp_content = response.json() + response_msg = resp_content.get("message") + response_status = int(resp_content.get("status")) + + if response_status == 401 and response_msg == "Bad credentials": + raise NotAuthorizedException(f"Could not authorize with the github api because key was invalid: {mask_key(self.key)}") + else: + self.logger.warning(f"Received a 401 response from github unrelated to bad credentials with message {response_msg}") if response.status_code == 410: response_msg = response.json().get("message") From f1ccf31d1d4e787cfda5066780013ac8c6a6182a Mon Sep 17 00:00:00 2001 From: Adrian Edwards Date: Mon, 22 Jun 2026 15:52:53 -0400 Subject: [PATCH 4/5] warn when a bad credential is received Signed-off-by: Adrian Edwards --- collectoss/tasks/github/util/github_data_access.py | 1 + 1 file changed, 1 insertion(+) diff --git a/collectoss/tasks/github/util/github_data_access.py b/collectoss/tasks/github/util/github_data_access.py index 12dbfe3c9..b3f54ff98 100644 --- a/collectoss/tasks/github/util/github_data_access.py +++ b/collectoss/tasks/github/util/github_data_access.py @@ -174,6 +174,7 @@ def make_request(self, url, method="GET", timeout=100): response_status = int(resp_content.get("status")) if response_status == 401 and response_msg == "Bad credentials": + self.logger.warning(f"Received a 401 response from github due to bad credential: {mask_key(self.key)}") raise NotAuthorizedException(f"Could not authorize with the github api because key was invalid: {mask_key(self.key)}") else: self.logger.warning(f"Received a 401 response from github unrelated to bad credentials with message {response_msg}") From 9413fb2cdd4e74cea3735d96b6b4e63d1e45c181 Mon Sep 17 00:00:00 2001 From: Adrian Edwards Date: Tue, 23 Jun 2026 08:38:50 -0400 Subject: [PATCH 5/5] retry NotAuthorized Exceptions per comment from a github staff person "Ensure that apps retry the request at least once after receiving a 401 error, but with a slight delay to allow the database to catch up." https://github.com/orgs/community/discussions/101661#discussioncomment-8342211 Signed-off-by: Adrian Edwards --- collectoss/tasks/github/util/github_data_access.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/collectoss/tasks/github/util/github_data_access.py b/collectoss/tasks/github/util/github_data_access.py index b3f54ff98..9eaf634fc 100644 --- a/collectoss/tasks/github/util/github_data_access.py +++ b/collectoss/tasks/github/util/github_data_access.py @@ -206,7 +206,14 @@ def make_request_with_retries(self, url, method="GET", timeout=100): try: return self.__make_request_with_retries(url, method, timeout) except RetryError as e: - raise e.last_attempt.exception() + last_exception = e.last_attempt.exception() + + # https://github.com/orgs/community/discussions/101661#discussioncomment-8342211 + # this suggests we should retry 401 exceptions at least once + if isinstance(last_exception, NotAuthorizedException): + self.expired_keys_for_request = [] + self.__handle_github_not_authorized_response() + raise last_exception def _decide_retry_policy(exception: Exception) -> bool: """Defines whether or not to retry a failed request based on the exception thrown @@ -232,10 +239,6 @@ def __make_request_with_retries(self, url, method="GET", timeout=100): except RatelimitException as e: self.__handle_github_ratelimit_response(e.response) raise e - except NotAuthorizedException as e: - self.expired_keys_for_request = [] - self.__handle_github_not_authorized_response() - raise e def __handle_github_not_authorized_response(self):