Error Handling

All Jira API errors raise typed exceptions with structured error data from the response.

Basic Usage

from pyjira import (
    JiraError,
    AuthenticationError,
    ForbiddenError,
    NotFoundError,
    ValidationError,
    RateLimitError,
    ServerError,
)

try:
    client.issues.get('INVALID-999')
except NotFoundError as e:
    print(e.status_code)      # 404
    print(e.error_messages)   # ['Issue does not exist or you do not have permission to see it.']
    print(e.errors)           # {}
    print(e.response)         # httpx.Response (for inspection)
except AuthenticationError:
    print('Check your email/token')
except RateLimitError as e:
    print(f'Rate limited. Retry after {e.retry_after} seconds')
except JiraError as e:
    print(f'Unexpected Jira error: {e}')

Exception Hierarchy

JiraError (base)
  +-- ValidationError      (400 Bad Request)
  +-- AuthenticationError  (401 Unauthorized)
  +-- ForbiddenError       (403 Forbidden)
  +-- NotFoundError        (404 Not Found)
  +-- RateLimitError       (429 Too Many Requests)
  +-- ServerError          (5xx Server Errors)

Exception

HTTP Status

When

JiraError

any

Base class for all Jira API errors

ValidationError

400

Bad request / invalid fields

AuthenticationError

401

Invalid or missing credentials

ForbiddenError

403

Insufficient permissions

NotFoundError

404

Resource does not exist

RateLimitError

429

Too many requests

ServerError

5xx

Jira server error

Exception Attributes

Every exception carries these attributes:

Attribute

Type

Description

status_code

int | None

HTTP status code

error_messages

list[str]

Human-readable error messages from Jira

errors

dict[str, str]

Field-level errors (e.g. {'summary': 'Field is required'})

response

httpx.Response | None

Raw response for advanced inspection

RateLimitError has one additional attribute:

Attribute

Type

Description

retry_after

int | None

Seconds to wait before retrying

Handling Rate Limits

import time

def get_issue_with_retry(client, key, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.issues.get(key)
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise
            wait = e.retry_after or 10
            time.sleep(wait)

Inspecting Raw Responses

For debugging, you can access the raw httpx.Response on any exception:

try:
    client.issues.create(fields={})
except ValidationError as e:
    print(e.response.status_code)
    print(e.response.headers)
    print(e.response.text)