---
updatedAt: 2026-08-30T23:14:17.000Z
agentTools:
  projectIndex: https://docs.checkhq.com/llms.txt
---

# Errors during payroll preview

In API version `2025-01-01`, users have the option to preview payrolls *synchronously* or *asynchronously*. For more information about payroll preview, refer to [Payroll Preview and Approval](https://docs.checkhq.com/docs/payroll-preview-and-approval).

When previewing payrolls *synchronously*, all errors are returned in the same fashion. The preview request will return a 400 response, with an error message that looks like the following:

```json 400 Error Response
{
    "error": {
        "type": "payroll_already_approved",
        "message": "Payroll is already approved, and further changes are not allowed"
    }
}
```

The above is how errors are returned in preview for all previous API versions (e.g., `2023-04-04`) as well.

When previewing payrolls *asynchronously*, errors may be returned in two different ways. Some errors will return in the same fashion as the synchronous preview errors above. However, other errors are not caught and returned until after the preview calculation is completed; these are errors that depend on the results of the preview calculation in order to be identified.

For this second category of errors, the asynchronous preview request will return an initial 200 response (with `"preview.status": "calculating"`, indicating that the preview is able to be successfully initiated), but on fetching the payroll after the preview is completed, the `preview` object on the payroll will display an error message. Here is an example:

```json Error messages in the preview object
{
  ...,
  "preview": {
    "messages": [
      {
        "severity": "aborted",
        "code": "3dp_cash_requirement_limit_exceeded",
        "message": "Payroll exceeds three-day payroll cash requirement limit of $1,000,000. To proceed, please change the payroll's funding_payment_method to 'wire'."
      }
    ],
    "status": "failed",
    "started_at": "2025-03-21T21:42:21.672559Z"
  }
}
```

`preview` error messages for asynchronous preview also contain a new field called `severity`. This field is intended to distinguish between errors which caused the payroll to be `aborted` (which indicates that the preview calculation was invalid, and is not saved on the payroll), and `blocking` (which indicates that the preview calculation was saved, but the payroll preview will not able to be approved unless the message is addressed).

Below are the list of errors that may be returned on payroll preview, whether they are surfaced in a 400 response or a `preview` error message during asynchronous preview, and if applicable, their `severity`:

<Table align={["left","left","left","left"]}>
  <thead>
    <tr>
      <th>
        Type
      </th>

      <th>
        Message
      </th>

      <th>
        Asynchronous Response Type
      </th>

      <th>
        Severity
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `payroll_already_approved`
      </td>

      <td>
        `Payroll is already approved, and further changes are not allowed`
      </td>

      <td>
        400 Response
      </td>

      <td>
        n/a
      </td>
    </tr>

    <tr>
      <td>
        `non_empty_payroll_required`
      </td>

      <td>
        `Payrolls must have at least one item or contractor payment to be previewed or approved`
      </td>

      <td>
        400 Response
      </td>

      <td>
        n/a
      </td>
    </tr>

    <tr>
      <td>
        `employee_missing_residence`
      </td>

      <td>
        `Employee {employee_id} does not have a residence.`
      </td>

      <td>
        400 Response
      </td>

      <td>
        n/a
      </td>
    </tr>

    <tr>
      <td>
        `employee_missing_workplaces`
      </td>

      <td>
        `Employee {employee_id} does not have any workplaces.`
      </td>

      <td>
        400 Response
      </td>

      <td>
        n/a
      </td>
    </tr>

    <tr>
      <td>
        `net_pay_negative`
      </td>

      <td>
        `One or more negative net pay errors occurred during handling of payroll {payroll.public_id}:`

        Followed by a list of the following:

        `Only imputed wage entered; Regular or supplement earnings required to cover taxes on imputed wage.`

        `Full pre-tax retirement benefit 401k amount cannot be withheld.`
      </td>

      <td>
        `preview` Error Message
      </td>

      <td>
        `aborted`
      </td>
    </tr>

    <tr>
      <td>
        `3dp_cash_requirement_limit_exceeded`
      </td>

      <td>
        `Payroll exceeds three-day payroll cash requirement limit of $1,000,000. To proceed, please change the payroll's funding_payment_method to 'wire'.`
      </td>

      <td>
        `preview` Error Message
      </td>

      <td>
        `aborted`
      </td>
    </tr>

    <tr>
      <td>
        `credit_limit_exceeded`
      </td>

      <td>
        `The payroll failed credit limit validation. To proceed, please change the payroll's funding_payment_method to 'wire' or update the payroll's processing_period to either 'three_day' or 'four_day' (note: updating the processing_period may also necessitate a change to the payroll's payday in order to provide adequate time for approval).`
      </td>

      <td>
        `preview` Error Message
      </td>

      <td>
        `aborted`
      </td>
    </tr>

    <tr>
      <td>
        `deduction_ytd_negative`
      </td>

      <td>
        `One or more deductions exceed YTD limits`
      </td>

      <td>
        `preview` Error Message
      </td>

      <td>
        `aborted`
      </td>
    </tr>

    <tr>
      <td>
        `entity_missing_required_value`
      </td>

      <td>
        `Entity missing required value.`
      </td>

      <td>
        `preview` Error Message
      </td>

      <td>
        `aborted`
      </td>
    </tr>

    <tr>
      <td>
        `payroll_locked`
      </td>

      <td>
        `Too many requests modifying payrolls in `{company_id}`. Try again in 60 seconds.`
      </td>

      <td>
        423 Response.
      </td>

      <td>
        n/a
      </td>
    </tr>
  </tbody>
</Table>

Today, all errors surfaced have a severity of `aborted`. However, Check may add new error types with a `blocking` severity, or recategorize the severity of existing errors over time. It is recommended that your integration with error messages in asynchronous preview read the `severity` field in order to determine whether preview calculation results are available to be displayed.