{"record":{"id":"4735967cd9fcf67f","repo":"infiniflow/ragflow","slug":"jira-rate-limit-exceeded-during-validation-http-4","errorCode":null,"errorMessage":"Jira rate limit exceeded during validation (HTTP 429).","messagePattern":"Jira rate limit exceeded during validation \\(HTTP 429\\)\\.","errorType":"validation","errorClass":"ConnectorValidationError","httpStatus":429,"severity":"warning","filePath":"common/data_source/jira/connector.py","lineNumber":287,"sourceCode":"                return (yield from self._load_from_checkpoint_internal(jql, checkpoint, start_filter=start))\n            except Exception as exc:\n                if attempt_start is not None and not retried_with_buffer and is_atlassian_date_error(exc):\n                    attempt_start = attempt_start - ONE_HOUR\n                    retried_with_buffer = True\n                    logger.info(f\"[Jira] Atlassian date error detected; retrying with start={attempt_start}.\")\n                    continue\n                raise\n\n    def _handle_validation_error(self, exc: Exception) -> None:\n        status_code = getattr(exc, \"status_code\", None)\n        if status_code == 401:\n            raise InsufficientPermissionsError(\"Jira credential appears to be invalid or expired (HTTP 401).\") from exc\n        if status_code == 403:\n            raise InsufficientPermissionsError(\"Jira token does not have permission to access the requested resources (HTTP 403).\") from exc\n        if status_code == 404:\n            raise ConnectorValidationError(\"Jira resource not found (HTTP 404).\") from exc\n        if status_code == 429:\n            raise ConnectorValidationError(\"Jira rate limit exceeded during validation (HTTP 429).\") from exc\n\n        message = getattr(exc, \"text\", str(exc))\n        if not message:\n            raise UnexpectedValidationError(\"Unexpected Jira validation error.\") from exc\n\n        raise ConnectorValidationError(f\"Jira validation failed: {message}\") from exc\n\n    def _load_from_checkpoint_internal(\n        self,\n        jql: str,\n        checkpoint: JiraCheckpoint,\n        start_filter: SecondsSinceUnixEpoch | None = None,\n    ) -> Generator[Document | ConnectorFailure, None, JiraCheckpoint]:\n        assert self.jira_client, \"load_credentials must be called before loading issues.\"\n\n        page_size = self._full_page_size()\n        new_checkpoint = copy.deepcopy(checkpoint)\n        starting_offset = new_checkpoint.start_at or 0","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/common/data_source/jira/connector.py#L269-L305","documentation":"ConnectorValidationError raised by _handle_validation_error when the Jira exception carries status_code == 429 - Atlassian rate limiting. Cloud enforces per-user and per-app request ceilings; the validation JQL search hit them and the server answered 429. Unlike auth errors, this is transient and resolves after the retry window.","triggerScenarios":"Running validate_connector_settings() (which issues a JQL search) while the same token is used by other jobs, bulk syncs, or monitoring that saturate the rate budget; tight retry loops without backoff.","commonSituations":"Many connectors sharing one service account; backfill jobs issuing rapid JQL pages; CI validation suites hammering the API; Atlassian tightening limits for the plan tier.","solutions":["Retry after waiting - honor the Retry-After header on the chained response if present, else wait 30-60s with exponential backoff.","Reduce parallelism: one in-flight job per token; add jitter to schedules so jobs do not align.","Dedicate separate tokens or service accounts to high-volume jobs.","If sustained, check the Atlassian rate-limit dashboard for the account's ceiling."],"exampleFix":"# before\nfor attempt in range(5):\n    connector.validate_connector_settings()  # immediate retries -> 429 storm\n\n# after\nimport time, random\nfor attempt in range(5):\n    try:\n        connector.validate_connector_settings()\n        break\n    except ConnectorValidationError as e:\n        if '429' not in str(e) or attempt == 4:\n            raise\n        time.sleep(min(60, 2 ** attempt) + random.random())","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"def validate_with_backoff(connector, max_attempts: int = 5):\n    for attempt in range(max_attempts):\n        try:\n            return connector.validate_connector_settings()\n        except ConnectorValidationError as e:\n            if 'HTTP 429' not in str(e) or attempt == max_attempts - 1:\n                raise\n            time.sleep(min(60, 2 ** attempt) + random.random())","preventionTips":["Honor Retry-After from the chained response when present.","Stagger sync schedules and cap concurrency per token to stay under rate ceilings.","Reserve separate service accounts for bulk backfills versus routine validation."],"tags":["jira","rate-limit","http-429","retry"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}