Skip to content

Documentation

Errors

Every error has the same shape, a stable machine-readable code, and — where we can work it out — a hint that says what to do about it.

The error envelope

Errors always come back as a single error object. Branch on code, which is stable within an API version. Do not branch on message, which we reword whenever we find a clearer way to say it.

422 Unprocessable Entity
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_failed",
    "message": "The request could not be processed. See details.",
    "request_id": "req_01J9ZT4N8K3M",
    "doc_url": "https://easarctech.com/docs/errors#validation_failed",
    "details": [
      {
        "field": "line_items[0].hsn",
        "code": "invalid_hsn",
        "message": "HSN 5407 requires 6 digits for your turnover slab. Use 540752."
      },
      {
        "field": "line_items[1].quantity.uom",
        "code": "invalid_enum",
        "message": "'mtr' is not a valid UQC code. Did you mean 'MTR'?"
      },
      {
        "field": "due_on",
        "code": "date_in_past",
        "message": "due_on must be on or after ordered_on."
      }
    ]
  }
}
  • type — a broad family: authentication_error, invalid_request_error, rate_limit_error, api_error.
  • code — the specific, stable identifier to switch on.
  • request_id — always present. Quote it to support and we can find the exact request in seconds.
  • details — present on validation failures, one entry per field, and we return all of them rather than stopping at the first.

HTTP statuses

HTTP status codes returned by the Easarc API
StatusMeaning
200Success
201Created
204Success, no body — deletions and acknowledgements
400The request itself is malformed
401Not authenticated
403Authenticated, but not permitted
404No such object, or not yours
409Conflicts with the current state of the resource
413Body larger than 1 MB
422Well-formed, but the values cannot be accepted
429Rate limited
500Our bug
503Temporarily unavailable, including upstream government portals
504Upstream timeout

Every error code

Complete list of Easarc error codes
CodeStatusMeaningRetry?
missing_credentials401No Authorization header was sentNo
invalid_api_key401The key does not exist or has been revokedNo
expired_token401The OAuth access token has expired — refresh itAfter refresh
insufficient_scope403The credential is valid but lacks the required scopeNo
environment_mismatch403A test key was used against production, or the reverseNo
account_suspended403The account is suspended, usually for non-paymentNo
resource_not_found404No object with that id, or it belongs to another accountNo
method_not_allowed405Wrong HTTP verb for this pathNo
invalid_request400Malformed JSON, or a query parameter that will not parseNo
unknown_field400A field we do not recognise — usually a typoNo
validation_failed422The request parsed but the values are not acceptableNo
duplicate_reference409An order already exists with that referenceNo
order_not_cancellable409The order has an invoice with an active IRNNo
stage_out_of_sequence409That stage cannot follow the current oneNo
idempotency_key_reused409The same key was sent with a different request bodyNo
eway_bill_active409Cancel the e-way bill before cancelling the IRNNo
irn_window_expired422More than 24 hours since IRN generation — issue a credit noteNo
irp_rejected422The government IRP rejected the invoice; see irp_errorsAfter fixing
irp_unavailable503The IRP is down or timing outYes
rate_limited429Too many requests — honour Retry-AfterYes
payload_too_large413Request body above 1 MBNo
internal_error500Our fault. Quote the request_id if it persistsYes
service_unavailable503Deploying or briefly degradedYes
gateway_timeout504An upstream took too longYes

GST e-invoice failures

When the IRP rejects an invoice we return its codes verbatim under irp_errors, because your CA will recognise them. Where we can tell what went wrong, we add a hint in plain language.

422 — IRP rejection
{
  "error": {
    "type": "invalid_request_error",
    "code": "irp_rejected",
    "message": "The Invoice Registration Portal rejected this invoice.",
    "request_id": "req_01J9ZT6P2R9V",
    "irp_errors": [
      {
        "code": "2172",
        "message": "For inter-state supply, IGST must be charged. CGST and SGST are not applicable."
      }
    ],
    "hint": "place_of_supply is 27 (Maharashtra) but the tax lines carry CGST and SGST. Set the tax type from place_of_supply, or correct the ship-to address."
  }
}

irp_rejected is never worth an automatic retry: the same payload will be rejected again. irp_unavailable is the opposite — the portal is simply down, and the request should be queued and retried.

Handling errors in practice

JavaScript
import { EasarcError } from '@easarc/sdk'

try {
  await easarc.orders.invoice(orderId, { generate_eway_bill: true })
} catch (error) {
  if (!(error instanceof EasarcError)) throw error

  switch (error.code) {
    case 'irp_rejected':
      // A human has to fix the data. Surface the hint, do not retry.
      await flagForReview(orderId, error.irpErrors, error.hint)
      break

    case 'irp_unavailable':
    case 'rate_limited':
      // Transient. The SDK has already retried; queue it for later.
      await retryQueue.add({ orderId }, { delay: 60_000 })
      break

    case 'duplicate_reference':
      // Already registered — reconcile instead of creating a second one.
      await reconcileExisting(error.details?.[0]?.existing_id)
      break

    default:
      throw error
  }
}
  • Retry only 429, 500, 503 and 504, with exponential backoff and jitter. Retrying a 422 just produces the same 422.
  • Always send an Idempotency-Key on writes, so a retry after a timeout cannot create a second order.
  • Log request_id alongside your own correlation id. It is the single most useful thing in a support conversation.
  • Treat irp_rejected as work for a person, not for a queue. Somebody has to correct an HSN or an address.