| layout | default | ||
|---|---|---|---|
| title | Error Handling | ||
| nav_order | 9 | ||
| description | Error handling patterns for DeepCitation in production | ||
| commit_sha | 31553cd | ||
| stale_after_commits | 15 | ||
| watch_paths |
|
Production patterns for handling DeepCitation errors gracefully.
| Operation | Can fail? | Common causes | Safe to retry? |
|---|---|---|---|
new DeepCitation({ apiKey }) |
Yes | Missing or empty API key | No -- fix the key |
uploadFile() / prepareAttachments() |
Yes | Network timeout, file too large, invalid format | Yes |
prepareUrl() |
Yes | Network timeout, URL unreachable, blocked by site | Yes (with backoff) |
verify() |
Yes | Network timeout, invalid citations, API error | Yes |
verifyAttachment() |
Yes | Network timeout, invalid attachment ID | Yes |
getAllCitationsFromLlmOutput() |
No | Never throws -- returns {} on failure |
N/A |
wrapCitationPrompt() |
No | Never throws -- returns enhanced prompts | N/A |
getCitationStatus() |
No | Never throws -- returns status object | N/A |
DeepCitation provides structured error classes for programmatic error handling:
{: .note }
verify({ llmOutput }) is a convenience wrapper — it parses citations from the raw LLM output (via getAllCitationsFromLlmOutput()), groups them by attachment, then verifies each group. Use verifyAttachment(attachmentId, citations) when you extract and manage citations yourself. See [SDK Reference]({{ site.baseurl }}/sdk-reference/) for full method signatures.
import {
AuthenticationError,
NetworkError,
PaymentRequiredError,
RateLimitError,
ValidationError,
ServerError,
DeepCitationError,
} from "deepcitation";
try {
const { verifications } = await dc.verify({ llmOutput });
} catch (err) {
if (err instanceof AuthenticationError) {
// API key is missing, invalid, or expired
// Status code: 401 or 403
// NOT retryable - fix the API key
console.error("Check your DEEPCITATION_API_KEY:", err.message);
} else if (err instanceof PaymentRequiredError) {
// Free tier exhausted, spend cap hit, or payment failed
// Status code: 402
// NOT retryable - add or update a payment method
console.error("Payment required:", err.message, "billing code:", err.billingCode);
} else if (err instanceof RateLimitError) {
// Hit rate limit (429)
// Retryable after delay
console.error("Rate limited, retry after delay:", err.message);
} else if (err instanceof ValidationError) {
// Bad request: invalid format, file too large, etc.
// Status codes: 400, 404, 413, etc.
// NOT retryable - fix the input
console.error("Validation error:", err.message);
} else if (err instanceof ServerError) {
// API returned 5xx error
// Retryable with backoff
console.error("Server error, safe to retry:", err.message);
} else if (err instanceof NetworkError) {
// Network failure: timeout, DNS, connection refused
// Retryable with backoff
console.error("Network error, safe to retry:", err.message);
}
}All errors extend DeepCitationError and include:
code- Machine-readable error code (see table below)isRetryable- Boolean flag indicating whether the operation can be safely retriedstatusCode- HTTP status code if applicabledocUrl- Link to documentation for this error code (e.g.,https://docs.deepcitation.com/errors#DC_AUTH_INVALID)
| Code | Error Class | HTTP Status | Retryable | Recovery Action |
|---|---|---|---|---|
DC_AUTH_INVALID |
AuthenticationError |
401, 403 | No | Check API key — rotate at deepcitation.com/keys |
DC_PAYMENT_REQUIRED |
PaymentRequiredError |
402 | No | Free tier exhausted, spend cap hit, or payment failed — add or update a payment method at deepcitation.com/pricing. Error includes a billingCode field with the server-side reason. |
DC_NETWORK_ERROR |
NetworkError |
— | Yes | Retry with exponential backoff — check network connectivity |
DC_RATE_LIMITED |
RateLimitError |
429 | Yes | Retry with exponential backoff — API rate limit hit |
DC_VALIDATION_ERROR |
ValidationError |
400, 404, 413 | No | Fix the input — check file size (max 100 MB), format, or attachment ID |
DC_SERVER_ERROR |
ServerError |
5xx | Yes | Retry with exponential backoff — if persistent, check status.deepcitation.com |
Use the isRetryable flag to determine which errors are safe to retry:
import { DeepCitationError } from "deepcitation";
async function withRetry<T>(
fn: () => Promise<T>,
{ maxRetries = 3, baseDelay = 1000 } = {}
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (err) {
const isLastAttempt = attempt === maxRetries;
// Only retry errors that are marked as retryable
if (err instanceof DeepCitationError && !err.isRetryable) {
throw err; // Don't retry auth or validation errors
}
if (isLastAttempt) throw err;
// Exponential backoff with jitter
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 500;
await new Promise((r) => setTimeout(r, delay));
}
}
throw new Error("Unreachable");
}
// Usage
const { verifications } = await withRetry(() =>
dc.verify({ llmOutput: response.content })
);
// Production guidance:
// - 3 retries is sufficient for transient errors (network blips, 503s)
// - Never retry auth errors (fix the key) or validation errors (fix the input)
// - For 402 billing errors (PaymentRequiredError), retrying won't help — add a payment method
// - If you see persistent 5xx errors after 3 retries, check status.deepcitation.comDeepCitation has two distinct limit types:
- Billing limits (
402 Payment Required→PaymentRequiredError): when your free tier is exhausted, a spend cap is reached, or payment fails. Not retryable — add or update a payment method at deepcitation.com/pricing. - API rate limits (
429 Too Many Requests→RateLimitError): when request throughput exceeds your plan's concurrency limits. Retryable with exponential backoff.
If you're processing many documents in parallel, use the built-in concurrency limiter:
// The client limits concurrent uploads to 5 by default
const results = await dc.prepareAttachments(manyFiles);- Maximum file size: Check current limits at deepcitation.com/pricing
- Supported formats: PDF, DOCX, XLSX, PPTX, HTML, JPG, PNG, TIFF, WebP, HEIC
- Files that exceed limits will return a
413error
If getAllCitationsFromLlmOutput() returns an empty object {}, check:
- Did you wrap the prompt? Use
wrapCitationPrompt()to add citation instructions to your LLM call - Is the LLM following the format? Check the raw LLM output for
<cite ... />tags or<<<CITATION_DATA>>>blocks - Did you pass the
deepTextPages? The LLM needs the source text thatwrapCitationPrompt()renders into citation-ready prompt text
const citations = getAllCitationsFromLlmOutput(llmOutput);
if (Object.keys(citations).length === 0) {
// No citations found -- check the raw output
console.log("Raw LLM output:", llmOutput);
console.log("Contains cite tags:", llmOutput.includes("<cite"));
console.log("Contains deferred block:", llmOutput.includes("<<<CITATION_DATA>>>"));
}If verify() returns { verifications: {} }, the client found no citations to verify. This is not an error -- it means getAllCitationsFromLlmOutput() found nothing in the LLM output. See "Empty citations" above.
"My citations aren't styled"
You forgot to import the stylesheet. Add @import "deepcitation/tailwind.css" to your CSS (Tailwind v4) or import "deepcitation/styles.css" in JS. See [Styling]({{ site.baseurl }}/styling/).
"Verification says not_found but the text is there"
The LLM likely paraphrased the source. Check if you got a partial_text_found or found_source_match_only status instead. See [Verification Statuses]({{ site.baseurl }}/verification-statuses/) for the full list of partial match statuses.
"I exposed my API key in the browser"
Rotate it immediately at deepcitation.com/keys. Never prefix your key with NEXT_PUBLIC_ or expose it in client-side code — all DeepCitation API calls should happen server-side.
"API key format"
Keys always start with sk-dc- and must be at least 20 characters. If you're getting authentication errors, check for trailing whitespace or newlines in your environment variable.
- [Getting Started]({{ site.baseurl }}/getting-started/)
- [API Reference]({{ site.baseurl }}/api-reference/)
- [Styling Guide]({{ site.baseurl }}/styling/)