Skip to content
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

KeyError: X-RateLimit-FillRate #1463

Closed
2 of 4 tasks
adhamselman opened this issue Aug 16, 2022 · 6 comments · Fixed by #1593
Closed
2 of 4 tasks

KeyError: X-RateLimit-FillRate #1463

adhamselman opened this issue Aug 16, 2022 · 6 comments · Fixed by #1593

Comments

@adhamselman
Copy link

adhamselman commented Aug 16, 2022

Bug summary

https://github.com/pycontribs/jira/blob/main/jira/resilientsession.py#L287
As far as i can tell, JIRA cloud does not set this response header when rate limiting. The only rate limiting headers i can see are: X-Ratelimit-Limit. X-Ratelimit-Remaining, X-Ratelimit-Reset and Retry-After.

Is there an existing issue for this?

  • I have searched the existing issues

Jira Instance type

Jira Cloud (Hosted by Atlassian)

Jira instance version

No response

jira-python version

3.3.2

Python Interpreter version

3.9

Which operating systems have you used?

  • Linux
  • macOS
  • Windows

Reproduction steps

# 1. Given a Jira client instance
jira: JIRA
# 2. When I call the function with argument x
jira.the_function(x)
# 3.
...

Stack trace

KeyError: X-RateLimit-FillRate

Expected behaviour

No KeyError and request is retried after 'retry-after' amount.

Additional Context

No response

@wesleyhamburger-okta
Copy link

Thanks for creating this issue. Is there any timeline for when a fix will be out for this?

@wesleyhamburger-okta
Copy link

Also, perhaps its worth noting that this was introduced in 3.3.0 not 3.3.2 -

3.2.0...3.3.0#diff-0605333df4b31ca7f97b2591e3bcba9c71ea201f4a88e383316e7f6c1c478e06R286

@BenKettlewell
Copy link

Encountering this problem as well. Here's a little more for a stack trace.

  File "...\mycode", line 123, in my_function
    self.jira.my_function(
  File "...\AppData\Roaming\Python\Python39\site-packages\jira-3.4.1-py3.9.egg\jira\client.py", line 119, in wrapper
    result = func(*arg_list, **kwargs)
  File "...\AppData\Roaming\Python\Python39\site-packages\jira-3.4.1-py3.9.egg\jira\client.py", line 2497, in create_issue_link
    return self._session.post(url, data=json.dumps(data))
  File "...\appdata\local\programs\python\python39\lib\site-packages\requests\sessions.py", line 590, in post
    return self.request('POST', url, data=data, json=json, **kwargs)
  File "...\AppData\Roaming\Python\Python39\site-packages\jira-3.4.1-py3.9.egg\jira\resilientsession.py", line 213, in request
    if is_allowed_to_retry() and self.__recoverable(
  File "...\AppData\Roaming\Python\Python39\site-packages\jira-3.4.1-py3.9.egg\jira\resilientsession.py", line 286, in __recoverable
    number_of_tokens_issued_per_interval = response.headers[
  File "...\appdata\local\programs\python\python39\lib\site-packages\requests\structures.py", line 54, in __getitem__
    return self._store[key.lower()][1]
KeyError: 'x-ratelimit-fillrate'

@rehsals
Copy link
Contributor

rehsals commented Jan 18, 2023

In my case the only headers to go with 429 error are:

{
    "content-length": "117",
    "cache-control": "no-cache",
    "content-type": "text/html"
}

The rest of the timeout logic works fine if I remove headers access. The only thing is this headers KeyError.

@edporteous
Copy link

edporteous commented Mar 3, 2023

In case it's helpful, I got this error and rich gave me this traceback, which includes the headers:

│ C:\Users\edp\AppData\Local\pypoetry\Cache\virtualenvs\trac-to-jira-ZN2GFehq-py3.10\lib\site-pack │
│ ages\requests\sessions.py:635 in post                                                            │
│                                                                                                  │
│   632 │   │   :rtype: requests.Response                                                          │
│   633 │   │   """                                                                                │
│   634 │   │                                                                                      │
│ ❱ 635 │   │   return self.request("POST", url, data=data, json=json, **kwargs)                   │
│   636 │                                                                                          │
│   637 │   def put(self, url, data=None, **kwargs):                                               │
│   638 │   │   r"""Sends a PUT request. Returns :class:`Response` object.                         │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │   data = '{"body": "xxx        │ │
│ │          xxx                                                                         │ │
│ │   json = None                                                                                │ │
│ │ kwargs = {}                                                                                  │ │
│ │   self = <jira.resilientsession.ResilientSession object at 0x000001CFADEEA1D0>               │ │
│ │    url = 'https://mydomain.atlassian.net/rest/api/2/issue/PM-809/comment'                     │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ C:\Users\edp\AppData\Local\pypoetry\Cache\virtualenvs\trac-to-jira-ZN2GFehq-py3.10\lib\site-pack │
│ ages\jira\resilientsession.py:213 in request                                                     │
│                                                                                                  │
│   210 │   │   │   # Decide if we should keep retrying                                            │
│   211 │   │   │   response_or_exception = response if response is not None else exception        │
│   212 │   │   │   retry_number += 1                                                              │
│ ❱ 213 │   │   │   if is_allowed_to_retry() and self.__recoverable(                               │
│   214 │   │   │   │   response_or_exception, url, method.upper(), retry_number                   │
│   215 │   │   │   ):                                                                             │
│   216 │   │   │   │   _prepare_retry_class.prepare(processed_kwargs)  # type: ignore[arg-type]   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │             __class__ = <class 'jira.resilientsession.ResilientSession'>                     │ │
│ │  _prepare_retry_class = <jira.resilientsession.PassthroughRetryPrepare object at             │ │
│ │                         0x000001CFAC1F6380>                                                  │ │
│ │             exception = None                                                                 │ │
│ │   is_allowed_to_retry = <function ResilientSession.request.<locals>.is_allowed_to_retry at   │ │
│ │                         0x000001CFAF832290>                                                  │ │
│ │                kwargs = {                                                                    │ │
│ │                         │   'data': '{"body": "xxx,                                       │ │
│ │                         │   'json': None                                                     │ │
│ │                         }                                                                    │ │
│ │                method = 'POST'                                                               │ │
│ │      processed_kwargs = {                                                                    │ │
│ │                         │   'data': '{"body": "{0,                                       │ │
│ │                         │   'json': None,                                                    │ │
│ │                         │   'headers': {'User-Agent': 'python-requests/2.28.1',              │ │
│ │                         'Accept-Encoding': 'gzip, deflate', 'Accept':                        │ │
│ │                         'application/json,*.*;q=0.9', 'Connection': 'keep-alive',            │ │
│ │                         'Cache-Control': 'no-cache', 'Content-Type': 'application/json',     │ │
│ │                         'X-Atlassian-Token': 'no-check'}                                     │ │
│ │                         }                                                                    │ │
│ │              response = <Response [429]>                                                     │ │
│ │ response_or_exception = <Response [429]>                                                     │ │
│ │          retry_number = 1                                                                    │ │
│ │                  self = <jira.resilientsession.ResilientSession object at                    │ │
│ │                         0x000001CFADEEA1D0>                                                  │ │
│ │                   url = 'https://mydomain.atlassian.net/rest/api/2/issue/PM-809/comment'      │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
C:\Users\edp\AppData\Local\pypoetry\Cache\virtualenvs\trac-to-jira-ZN2GFehq-py3.10\lib\site-pack │
│ ages\jira\resilientsession.py:286 in __recoverable                                               │
│                                                                                                  │
│   283 │   │   if isinstance(response, Response):                                                 │
│   284 │   │   │   if response.status_code in [429]:                                              │
│   285 │   │   │   │   is_recoverable = True                                                      │
│ ❱ 286 │   │   │   │   number_of_tokens_issued_per_interval = response.headers[                   │
│   287 │   │   │   │   │   "X-RateLimit-FillRate"                                                 │
│   288 │   │   │   │   ]                                                                          │
│   289 │   │   │   │   token_issuing_rate_interval_seconds = response.headers[                    │
│                                                                                                  │
│ ╭──────────────────────────────────────── locals ────────────────────────────────────────╮       │
│ │        counter = 1                                                                     │       │
│ │ is_recoverable = True                                                                  │       │
│ │            msg = '<Response [429]>'                                                    │       │
│ │ request_method = 'POST'                                                                │       │
│ │       response = <Response [429]>                                                      │       │
│ │           self = <jira.resilientsession.ResilientSession object at 0x000001CFADEEA1D0> │       │
│ │            url = 'https://mydomain.atlassian.net/rest/api/2/issue/PM-809/comment'       │       │
│ ╰────────────────────────────────────────────────────────────────────────────────────────╯       │
│                                                                                                  │
│ C:\Users\edp\AppData\Local\pypoetry\Cache\virtualenvs\trac-to-jira-ZN2GFehq-py3.10\lib\site-pack │
│ ages\requests\structures.py:52 in __getitem__                                                    │
│                                                                                                  │
│    49 │   │   self._store[key.lower()] = (key, value)                                            │
│    50 │                                                                                          │
│    51 │   def __getitem__(self, key):                                                            │
│ ❱  52 │   │   return self._store[key.lower()][1]                                                 │
│    53 │                                                                                          │
│    54 │   def __delitem__(self, key):                                                            │
│    55 │   │   del self._store[key.lower()]                                                       │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │  key = 'X-RateLimit-FillRate'                                                                │ │
│ │ self = {'Date': 'Fri, 03 Mar 2023 17:16:58 GMT', 'Content-Type':                             │ │
│ │        'application/json;charset=UTF-8', 'Server': 'AtlassianEdge', 'Timing-Allow-Origin':   │ │
│ │        '*', 'X-Arequestid': '72d13a2f5f541fb91d7f7c41eca8c502', 'X-Aaccountid':              │ │
│ │        '633371672eaaa5dcfa14bdf8', 'Cache-Control': 'no-cache, no-store, no-transform',      │ │
│ │        'Expect-Ct':                                                                          │ │
│ │        'report-uri="https://web-security-reports.services.atlassian.com/expect-ct-report/at… │ │
│ │        max-age=86400', 'X-Content-Type-Options': 'nosniff', 'X-Xss-Protection': '1;          │ │
│ │        mode=block', 'Atl-Traceid': '87b7ede713a91672', 'Report-To': '{"endpoints": [{"url":  │ │
│ │        "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1",                      │ │
│ │        "include_subdomains": true, "max_age": 600}', 'Nel': '{"failure_fraction": 0.001,     │ │
│ │        "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}',              │ │
│ │        'Strict-Transport-Security': 'max-age=63072000; includeSubDomains; preload',          │ │
│ │        'Transfer-Encoding': 'chunked'}                                                       │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
KeyError: 'x-ratelimit-fillrate'

@adehad adehad linked a pull request Mar 11, 2023 that will close this issue
@adehad
Copy link
Contributor

adehad commented Mar 11, 2023

Hopefully in a release later this weekend

@adehad adehad closed this as completed Mar 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants