actualbudget/actual · error · RequisitionNotLinked

Requisition not linked yet

Error message

Requisition not linked yet

What it means

gocardless-service fetches a requisition and only continues when its status is 'LN' (linked), meaning the end user finished the bank authorization. Any other status throws RequisitionNotLinked carrying the actual requisitionStatus.

Source

Thrown at packages/sync-server/src/app-gocardless/services/gocardless-service.ts:144

      }
    };

    if (isExpiredJwtToken(getGocardlessClient().token)) {
      await client.generateToken().catch(handleGoCardlessError);
    }
  },

  getLinkedRequisition: async (
    requisitionId: GoCardlessRequisitionId,
  ): Promise<Requisition> => {
    const requisition = await goCardlessService.getRequisition(requisitionId);

    const { status } = requisition;

    // Continue only if status of requisition is "LN" which
    // means the account has been successfully linked to the requisition
    if (status !== 'LN') {
      throw new RequisitionNotLinked({ requisitionStatus: status });
    }

    return requisition;
  },

  getRequisitionWithAccounts: async (
    requisitionId: GoCardlessRequisitionId,
  ): Promise<{
    requisition: Requisition;
    accounts: NormalizedAccountDetails[];
  }> => {
    const requisition =
      await goCardlessService.getLinkedRequisition(requisitionId);

    console.log('GoCardless requisition linked:', {
      institutionId: requisition.institution_id,
      requisitionId,
      agreementId: requisition.agreement,

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Have the user re-run the link flow (create a new requisition and complete bank authorization) to reach status LN
  2. Inspect the returned requisitionStatus to decide the message shown to the user (expired vs rejected vs pending)
  3. If status is CR, send the user to the requisition link URL to finish authorization
  4. Delete the stale requisition/account in the UI and re-add the bank connection

Example fix

// before
const accounts = await gocardlessService.getRequisitionWithAccounts(reqId); // throws if not LN
// after
const req = await gocardlessService.getRequisition(reqId);
if (req.status !== 'LN') {
  // prompt user to re-authenticate / re-create the link
  await restartLinkFlow(institutionId);
  return;
}
const accounts = await gocardlessService.getRequisitionWithAccounts(reqId);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const requisition = await getRequisition(reqId);
} catch (e) {
  if (e instanceof RequisitionNotLinked) {
    switch (e.details?.requisitionStatus) {
      case 'CR': promptUserToFinishBankAuth(e.details.link); break;
      case 'EX': case 'RJ': case 'GC':
      default: await recreateRequisition(institutionId); // re-link flow
    }
  } else throw e;
}

Prevention

When it happens

Trigger: Polling/reading accounts for a requisition whose status is e.g. 'RJ' (rejected/suspended), 'EX' (expired), 'CR' (created but not yet continued), or 'GC' (cancelled) — i.e. the user never completed (or failed) the bank consent flow.

Common situations: User closed the bank auth window without approving; bank rejected the PSU or the consent expired; server tried to sync a bank connection before the user finished linking; requisition link expired after 90 days per GoCardless.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/0b5b7b8962335405. Report an issue: GitHub.