BrandApp Playground

Error Handling

Throw each SDK error class to inspect status, code, context, and diagnostic. The same shape arrives in your real catch blocks.

Click any error class above to throw it and inspect the captured fields.
error-classes.ts
import {
  BadRequestError,     // 400
  AuthError,           // 401
  ForbiddenError,      // 403
  NotFoundError,       // 404
  ConflictError,       // 409
  ValidationError,     // 422
  RateLimitError,      // 429
  ServerError,         // 500
  // AI-specific subclasses (all extend ReoptSDKError)
  CreditLimitError,    // 402 — AI credit balance exhausted
  ModelAccessError,    // 403 — model not allowed for this plan tier
  ModelNotFoundError,  // 404 — unknown model id
  ContentFilterError,  // 422 — blocked by moderation policy
} from "@reopt-ai/brandapp-sdk";

try {
  await sdk.eav.records.get("ent_123", "rec_missing");
} catch (error) {
  // Use `instanceof` to branch on specific HTTP semantics
  if (error instanceof NotFoundError) {
    return null; // soft-fail
  }
  if (error instanceof ConflictError) {
    // Refetch and retry
  }
  if (error instanceof RateLimitError) {
    // Back off; `error.diagnostic.fix` includes a retry-after hint
  }
  if (error instanceof ValidationError) {
    return error.toFieldMap();
  }
  if (error instanceof CreditLimitError) {
    // 402 — prompt the user to top up AI credits
  }
  if (error instanceof ModelAccessError) {
    // 403 — fall back to an allowed model or prompt a plan upgrade
  }
  throw error;
}
is-reopt-sdk-error.ts
import { isReoptSDKError } from "@reopt-ai/brandapp-sdk";

try {
  await sdk.eav.records.create(entityId, { values: {} });
} catch (error) {
  if (isReoptSDKError(error)) {
    console.log(error.message);    // Human-readable message (with [METHOD URL] prefix)
    console.log(error.code);       // Error code (e.g. "VALIDATION_ERROR", "NOT_FOUND")
    console.log(error.status);     // HTTP status code (400, 403, 404, ...)
    console.log(error.context);    // { method, url, attempt? } — request context
    console.log(error.diagnostic); // { cause, fix, envVar?, doc? } — agent-readable hints
  }
}
is-validation-error.ts
import { isValidationError } from "@reopt-ai/brandapp-sdk";

try {
  await sdk.eav.records.create(entityId, { values: {} });
} catch (error) {
  if (isValidationError(error)) {
    // fieldErrors is an array of { field, message, code? }
    error.fieldErrors.forEach((fe) => {
      console.log(`${fe.field}: ${fe.message}`);
    });
    // "name: 필수 필드입니다"
    // "email: 올바른 이메일 형식이 아닙니다"

    // Get a single field's message
    const emailError = error.getFieldError("email");

    // Or convert to a flat record for form libraries
    const fieldMap = error.toFieldMap();
    // { name: "필수 필드입니다", email: "올바른 이메일 형식이 아닙니다" }
  }
}
diagnostic.ts
import { isCreditLimitError } from "@reopt-ai/brandapp-sdk";

// SDK errors carry agent-readable diagnostics under `error.diagnostic`.
// Every built-in error class populates this automatically.
type ErrorDiagnostic = {
  cause: string;     // Human-readable cause
  fix: string;       // Step the developer (or AI agent) should take
  envVar?: string;   // Related env var (when applicable)
  doc?: string;      // Path inside @reopt-ai/brandapp-sdk/docs/
};

// Example: AI credits exhausted (CreditLimitError, HTTP 402)
if (isCreditLimitError(error)) {
  console.log(error.diagnostic?.cause); // "BrandApp AI 크레딧 잔액 부족"
  console.log(error.diagnostic?.fix);   // "Studio → BrandApp → AI 크레딧 페이지에서 충전"
  console.log(error.diagnostic?.doc);   // "docs/errors.md#credit_limit"
}
lib/api-error.ts (enhanced)
import { NextResponse } from "next/server";
import {
  isReoptSDKError,
  isValidationError,
} from "@reopt-ai/brandapp-sdk";

// Centralized API error handler — maps SDK errors to HTTP responses.
export function handleApiError(error: unknown): NextResponse {
  if (isValidationError(error)) {
    return NextResponse.json(
      {
        error: error.message,
        code: error.code,
        fieldErrors: error.toFieldMap(),
        diagnostic: error.diagnostic,
      },
      { status: error.status },
    );
  }

  if (isReoptSDKError(error)) {
    return NextResponse.json(
      {
        error: error.message,
        code: error.code,
        diagnostic: error.diagnostic,
      },
      { status: error.status },
    );
  }

  const message =
    error instanceof Error ? error.message : "Internal Server Error";
  return NextResponse.json({ error: message }, { status: 500 });
}