Skip to content

Error Handling

Ivan William edited this page Aug 29, 2026 · 1 revision

Error Handling

Error classification and handling strategies for SATUSEHAT FHIR API integration.


SSResponse Error Detection

const resp = await ss.post('Patient', patientPayload);

if (!resp.isSuccess()) {
  for (const msg of resp.getErrorMessages()) {
    console.log(`API Error: ${msg}`);
  }
}

Error Categories

import { ErrorCategory } from '@satusehat/integration-node';

const errCategory = ErrorClassifier.classify(resp);

switch (errCategory) {
  case ErrorCategory.DUPLICATE:
    // Resource already exists
    break;
  case ErrorCategory.VALIDATION:
    // Field validation failed
    break;
  case ErrorCategory.TERMINOLOGY:
    // Code not in valueset
    break;
  case ErrorCategory.REFERENCE:
    // Reference resolution failed
    break;
  case ErrorCategory.AUTHENTICATION:
    await oauth2.refreshToken();
    break;
  case ErrorCategory.RATE_LIMIT:
    // Wait and retry
    break;
  case ErrorCategory.SERVER:
    // Retry with backoff
    break;
}
Category HTTP Description Action
Duplicate 400 Resource already sent Check existence first
Validation 400 Missing/invalid field Fix payload
Terminology 400 Invalid code SATUSEHAT valueset
Reference 400 Missing referenced resource Create prerequisite first
Authentication 401 Token expired Refresh token
Authorization 403 Insufficient permissions Check scopes
NotFound 404 Resource not found Verify IHS
RateLimit 429 Too many requests Wait Retry-After
Server 5xx Server error Retry with backoff

Parsing OperationOutcome

const body = resp.getBody();
const issues = body['issue'] as Array<Record<string, unknown>>;

for (const issue of issues) {
  const severity = issue['severity'] as string;
  const details = (issue['details'] as Record<string, unknown>)['text'] as string;
  const expressions = issue['expression'] as string[] | undefined;

  console.log(`[${severity}] ${details}`);
  if (expressions) {
    for (const expr of expressions) {
      console.log(`  Field: ${expr}`);
    }
  }
}

Retry with Error Classification

async function sendWithRetry(
  ss: SSRequest,
  resource: string,
  payload: Record<string, unknown>,
  maxRetries = 5
): Promise<SSResponse> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const resp = await ss.post(resource, payload);

    if (resp.isSuccess()) return resp;

    const category = ErrorClassifier.classify(resp);

    // Non-retryable — return immediately
    if (['DUPLICATE', 'VALIDATION', 'TERMINOLOGY', 'REFERENCE', 'AUTHORIZATION', 'NOT_FOUND'].includes(category)) {
      return resp;
    }

    // Retry with backoff
    const backoff = Math.min(2 ** attempt, 60) * 1000;
    await new Promise(resolve => setTimeout(resolve, backoff));
  }

  throw new Error('Max retries exceeded');
}

RateLimiter

import { RateLimiter } from '@satusehat/integration-node';

const limiter = new RateLimiter({ rpm: 60, burst: 10 });

for (const patient of patients) {
  await limiter.wait();
  const resp = await ss.post('Patient', patient);
}

Best Practices

  1. Always check isSuccess() — HTTP 200 doesn't guarantee API success
  2. Extract RuleNumber from error messages
  3. Prerequisite chain: Organization → Location → Practitioner → Patient → Encounter → Clinical
  4. Retry on 429/5xx — never retry on 4xx
  5. Use RateLimiter — prevent 429 proactively
  6. Bundle transactions — batch related resources

Clone this wiki locally