Skip to content

Commit 1456ae3

Browse files
committed
fix(auth): šŸ› improve credential refresh detection and prevent queue processor stuck state
Enhanced the proactive token refresh logic in both IFlowAuthBase and QwenAuthBase to more robustly detect OAuth credentials versus direct API keys: - Changed from checking file existence/env:// prefix to attempting credential load in try/except block - Added comprehensive debug logging throughout the refresh flow to track credential lifecycle - Fixed BUG#6 where queued credentials were not cleared on queue processor timeout, potentially causing stuck state - Now clears both unavailable_credentials and queued_credentials when processor times out The previous approach of checking `is_env_path` and `os.path.isfile()` could incorrectly classify credentials. The new approach leverages the existing `_load_credentials()` exception handling to make a definitive determination.
1 parent 219a7a9 commit 1456ae3

File tree

2 files changed

+52
-14
lines changed

2 files changed

+52
-14
lines changed

ā€Žsrc/rotator_library/providers/iflow_auth_base.pyā€Ž

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -749,15 +749,28 @@ async def proactively_refresh(self, credential_identifier: str):
749749
Proactively refreshes tokens if they're close to expiry.
750750
Only applies to OAuth credentials (file paths or env:// paths). Direct API keys are skipped.
751751
"""
752-
# Check if it's an env:// virtual path (OAuth credentials from environment)
753-
is_env_path = credential_identifier.startswith("env://")
752+
lib_logger.debug(f"proactively_refresh called for: {credential_identifier}")
754753

755-
# Only refresh if it's an OAuth credential (file path or env:// path)
756-
if not is_env_path and not os.path.isfile(credential_identifier):
757-
return # Direct API key, no refresh needed
754+
# Try to load credentials - this will fail for direct API keys
755+
# and succeed for OAuth credentials (file paths or env:// paths)
756+
try:
757+
creds = await self._load_credentials(credential_identifier)
758+
except IOError as e:
759+
# Not a valid credential path (likely a direct API key string)
760+
lib_logger.debug(
761+
f"Skipping refresh for '{credential_identifier}' - not an OAuth credential: {e}"
762+
)
763+
return
758764

759-
creds = await self._load_credentials(credential_identifier)
760-
if self._is_token_expired(creds):
765+
is_expired = self._is_token_expired(creds)
766+
lib_logger.debug(
767+
f"Token expired check for '{Path(credential_identifier).name}': {is_expired}"
768+
)
769+
770+
if is_expired:
771+
lib_logger.debug(
772+
f"Queueing refresh for '{Path(credential_identifier).name}'"
773+
)
761774
# Queue for refresh with needs_reauth=False (automated refresh)
762775
await self._queue_refresh(
763776
credential_identifier, force=False, needs_reauth=False
@@ -861,6 +874,12 @@ async def _process_refresh_queue(self):
861874
f"stale unavailable credentials: {list(self._unavailable_credentials.keys())}"
862875
)
863876
self._unavailable_credentials.clear()
877+
# [FIX BUG#6] Also clear queued credentials to prevent stuck state
878+
if self._queued_credentials:
879+
lib_logger.debug(
880+
f"Clearing {len(self._queued_credentials)} queued credentials on timeout"
881+
)
882+
self._queued_credentials.clear()
864883
self._queue_processor_task = None
865884
return
866885

ā€Žsrc/rotator_library/providers/qwen_auth_base.pyā€Ž

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,15 +476,28 @@ async def proactively_refresh(self, credential_identifier: str):
476476
Proactively refreshes tokens if they're close to expiry.
477477
Only applies to OAuth credentials (file paths or env:// paths). Direct API keys are skipped.
478478
"""
479-
# Check if it's an env:// virtual path (OAuth credentials from environment)
480-
is_env_path = credential_identifier.startswith("env://")
479+
lib_logger.debug(f"proactively_refresh called for: {credential_identifier}")
481480

482-
# Only refresh if it's an OAuth credential (file path or env:// path)
483-
if not is_env_path and not os.path.isfile(credential_identifier):
484-
return # Direct API key, no refresh needed
481+
# Try to load credentials - this will fail for direct API keys
482+
# and succeed for OAuth credentials (file paths or env:// paths)
483+
try:
484+
creds = await self._load_credentials(credential_identifier)
485+
except IOError as e:
486+
# Not a valid credential path (likely a direct API key string)
487+
lib_logger.debug(
488+
f"Skipping refresh for '{credential_identifier}' - not an OAuth credential: {e}"
489+
)
490+
return
485491

486-
creds = await self._load_credentials(credential_identifier)
487-
if self._is_token_expired(creds):
492+
is_expired = self._is_token_expired(creds)
493+
lib_logger.debug(
494+
f"Token expired check for '{Path(credential_identifier).name}': {is_expired}"
495+
)
496+
497+
if is_expired:
498+
lib_logger.debug(
499+
f"Queueing refresh for '{Path(credential_identifier).name}'"
500+
)
488501
# Queue for refresh with needs_reauth=False (automated refresh)
489502
await self._queue_refresh(
490503
credential_identifier, force=False, needs_reauth=False
@@ -587,6 +600,12 @@ async def _process_refresh_queue(self):
587600
f"stale unavailable credentials: {list(self._unavailable_credentials.keys())}"
588601
)
589602
self._unavailable_credentials.clear()
603+
# [FIX BUG#6] Also clear queued credentials to prevent stuck state
604+
if self._queued_credentials:
605+
lib_logger.debug(
606+
f"Clearing {len(self._queued_credentials)} queued credentials on timeout"
607+
)
608+
self._queued_credentials.clear()
590609
self._queue_processor_task = None
591610
return
592611

0 commit comments

Comments
Ā (0)