Enforcement

Handling blocks

When a budget is configured with action: "block" and a user hits their limit, the SDK throws a LLMBudgetExceededError. This page covers how to catch it, what information it contains, and how to return a graceful response to your user.

Start with warn, not block. The warn action lets calls proceed while still logging enforcement events and firing your callback. It's the safer default while you're getting set up — upgrade to block once you've added the error handling below.

Catching the error

Wrap your trackedCall() in a try/catch and check for LLMBudgetExceededError specifically. This lets you handle budget blocks differently from other errors.

import { trackedCall, LLMBudgetExceededError } from '@llmcosttracker/sdk'try {const response = await trackedCall({client, params, apiKey, userId: session.userId, tier: session.plan,})return response} catch (err) {if (err instanceof LLMBudgetExceededError) {// Return a graceful response to your userreturn {error: 'monthly_limit_reached', message: 'You have reached your monthly AI usage limit.',}}// Re-throw anything elsethrow err}

Error properties

The LLMBudgetExceededError instance contains everything you need to build a useful response:

err.codestringAlways "BUDGET_EXCEEDED". Useful for programmatic checks.
err.userIdstringThe user ID whose budget was exceeded.
err.limitUsdnumberThe configured spend limit in USD.
err.spendUsdnumberThe user's actual spend at the time of the block.
err.windowTypestring"day" | "week" | "month" — the window the limit applies to.
err.windowStartDateThe start of the current window. Use this to tell the user when their limit resets.
err.actionstringAlways "block" when this error is thrown.

Telling the user when their limit resets

Use err.windowStart and err.windowType to calculate the reset date and surface it in your UI:

import { LLMBudgetExceededError } from '@llmcosttracker/sdk'function getResetDate(err: LLMBudgetExceededError): Date {const start = new Date(err.windowStart)if (err.windowType === 'day') {start.setUTCDate(start.getUTCDate() + 1)} else if (err.windowType === 'week') {start.setUTCDate(start.getUTCDate() + 7)} else {start.setUTCMonth(start.getUTCMonth() + 1)}return start}// In your catch block:if (err instanceof LLMBudgetExceededError) {const resetDate = getResetDate(err)return {error: 'monthly_limit_reached', message: `Your AI usage limit resets on ${resetDate.toLocaleDateString()}`,}}

Using onBudgetWarning as an early signal

Even with action: "block", you can use the onBudgetWarning callback to get notified when a user hits your alert threshold — before they're blocked. Use this to show an in-app warning or send a notification proactively.

await trackedCall({client, params, apiKey, userId: session.userId, tier: session.plan, onBudgetWarning: (result) => {
    // Fires at alert threshold (default 80%) — before the block
    notifyUser(result.budgetConfig.userId, {
      message: `You've used $${result.spendUsd.toFixed(2)} of your $${result.limitUsd} limit`,
    })},})

Testing enforcement locally

Use action: "dry_run" while developing. The call always proceeds, but the SDK logs a console warning whenever it would have blocked. This lets you validate your budget config and error handling before flipping to block in production.

// dashboard config: action = dry_run// console output when limit would be hit:// [llmcosttracker] dry_run: would have blocked user "user_123"// ($5.0023 of $5.00 month limit)

Back to: Enforcement overview