CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/API & SDK
API & SDK/2026-05-22Intermediate

Designing a Model-Selection Fallback That Survives this model is currently unavailable on Claude API

The 'this model is currently unavailable' error from Claude API behaves nothing like a 529 Overloaded or a rate limit. After six months running it across six auto-publishing pipelines as an indie developer at Dolice, I'm sharing the failure conditions I observed and the per-request model-fallback implementation that ended my weekend firefighting.

claude-api81model-availability2fallback8error-handling11troubleshooting87

You shipped the auto-publishing agent in the evening, and an hour later it starts returning "this model is currently unavailable". Retries change nothing. An hour later it recovers, and then the same thing happens again at midnight. I have lived this loop on six auto-publishing pipelines I run as an indie developer at Dolice, especially during the weekend right after a new Claude model lands.

This one is not a 529 Overloaded and not a 429 rate limit. It is a per-model availability error tied to the exact model id you sent. Retrying with the same id will not fix it — you have to switch the id. So the right design is not "back off and wait" but "step down to a different model on the same request".

Here is what I learned about when it fires, and the fallback pattern that finally stopped my pipelines from going dark.

Five conditions where the error actually fires

Going back through six months of logs and support tickets, almost everything falls into five buckets:

  1. First few hours after a new model release: when Claude Sonnet 4.6 rolled out, an older SDK using the new id was unstable until the region-by-region rollout completed
  2. Deprecation window for an older model id: ids with an announced deprecation date can return availability errors intermittently in the days leading up to the cutoff
  3. Region-specific missing variants: across AWS Bedrock or Vertex AI a model may be available in us-west-2 but not ap-northeast-1. Less common on Anthropic direct API, but real on multi-cloud setups
  4. Invalid combination of beta feature flags: enabling extended thinking + parallel tool_use + computer use at the same time can fall outside the active rollout for some hours
  5. Account-tier permission violation: a Tier 1 account requesting a Tier 2 model — the rejection comes back through this same message

My own incident split was roughly 40% / 15% / 5% / 25% / 15%. Cases 1 and 4 together cover two thirds of the incidents, so they are where the engineering pays off.

A static fallback table per task tier

The single most effective fix is having a declared fallback chain for each task. I keep them keyed by tier.

type ModelTier = "frontier" | "balanced" | "fast";
 
interface ModelFallbackChain {
  primary: string;
  fallbacks: string[];
}
 
const FALLBACK_CHAINS: Record<ModelTier, ModelFallbackChain> = {
  frontier: {
    primary: "claude-opus-4-6",
    fallbacks: ["claude-sonnet-4-6", "claude-sonnet-4-5"],
  },
  balanced: {
    primary: "claude-sonnet-4-6",
    fallbacks: ["claude-sonnet-4-5", "claude-haiku-4-5-20251001"],
  },
  fast: {
    primary: "claude-haiku-4-5-20251001",
    fallbacks: ["claude-haiku-4-5", "claude-haiku-4"],
  },
};

Each task says "I want frontier — but rather than stop, drop down to balanced". After this change my pipeline's average downtime during a Claude Sonnet 4.6 weekend release window went from roughly 3 hours to under 8 minutes. That alone is the most impactful resilience change I made this year, and the implementation is under 50 lines.

Distinguishing the error and stepping through the chain

The HTTP status sits in the 400 family, so you can't fall back on status code alone. Inspect the SDK's structured error body.

import Anthropic from "@anthropic-ai/sdk";
 
async function invokeWithFallback(
  client: Anthropic,
  tier: ModelTier,
  buildRequest: (modelId: string) => Anthropic.Messages.MessageCreateParams
): Promise<Anthropic.Messages.Message> {
  const chain = FALLBACK_CHAINS[tier];
  const candidates = [chain.primary, ...chain.fallbacks];
  let lastError: unknown;
 
  for (const modelId of candidates) {
    try {
      const req = buildRequest(modelId);
      return await client.messages.create(req);
    } catch (err) {
      if (isModelUnavailable(err)) {
        console.warn(`model ${modelId} unavailable, trying next`);
        lastError = err;
        continue;
      }
      // Any other error: do not silently fall back. Throw upward.
      throw err;
    }
  }
  throw lastError;
}
 
function isModelUnavailable(err: unknown): boolean {
  if (!err || typeof err !== "object") return false;
  const e = err as { status?: number; error?: { type?: string; message?: string } };
  if (e.status !== 400 && e.status !== 404) return false;
  const msg = e.error?.message?.toLowerCase() ?? "";
  return (
    msg.includes("currently unavailable") ||
    msg.includes("model is not available") ||
    msg.includes("model_not_available") ||
    e.error?.type === "model_not_found_error"
  );
}

The discipline here is to scope the fallback to availability errors only. If you also catch rate limits or 529s and step down to a smaller model, you just propagate the upstream pressure to the smaller pool. Keep the predicate narrow.

Capability matrix so a fallback target doesn't reject features

Cause 4 (invalid feature-flag combination) is what trips fallback chains in production. My Lacrima and Mystery blog pipelines run extended thinking together with parallel tool_use; on Sonnet 4.6 it works, but on the 4.5 fallback the same combination intermittently returns availability errors. The fix is keeping a separate capability map for each model and stripping features when stepping down.

interface ModelCapabilities {
  modelId: string;
  extendedThinking: boolean;
  parallelToolUse: boolean;
  computerUse: boolean;
}
 
const CAPABILITY_MATRIX: Record<string, ModelCapabilities> = {
  "claude-opus-4-6": {
    modelId: "claude-opus-4-6",
    extendedThinking: true,
    parallelToolUse: true,
    computerUse: true,
  },
  "claude-sonnet-4-6": {
    modelId: "claude-sonnet-4-6",
    extendedThinking: true,
    parallelToolUse: true,
    computerUse: false,
  },
  "claude-sonnet-4-5": {
    modelId: "claude-sonnet-4-5",
    extendedThinking: true,
    parallelToolUse: true,
    computerUse: false,
  },
  "claude-haiku-4-5-20251001": {
    modelId: "claude-haiku-4-5-20251001",
    extendedThinking: false,
    parallelToolUse: true,
    computerUse: false,
  },
};
 
function buildRequestForModel(
  modelId: string,
  basePrompt: string,
  tools: Anthropic.Messages.Tool[]
): Anthropic.Messages.MessageCreateParams {
  const caps = CAPABILITY_MATRIX[modelId];
  return {
    model: modelId,
    max_tokens: 4096,
    messages: [{ role: "user", content: basePrompt }],
    tools,
    ...(caps.extendedThinking ? { thinking: { type: "enabled", budget_tokens: 16384 } } : {}),
  };
}

The whole point is: "I'd rather drop extended thinking than fail completely". The last-ditch model in the chain has the fewest features enabled, optimized for "still responds, even if a bit less smart".

Account-tier rejections need a separate alert path

Cause 5 doesn't recover via fallback. A Tier 1 account asking for a Tier 2 model has to either upgrade or use a different key. If you let it flow through the same fallback loop, your production logs fill with noise. I split this out as its own predicate and route it to a Slack alert.

function isAccountTierViolation(err: unknown): boolean {
  if (!err || typeof err !== "object") return false;
  const e = err as { error?: { message?: string } };
  const msg = e.error?.message?.toLowerCase() ?? "";
  return (
    msg.includes("not authorized") ||
    msg.includes("tier") ||
    msg.includes("upgrade your account")
  );
}

Tier violations get their own retry-zero, alert-immediately path in my pipeline. They never enter the fallback loop.

Observing how often the chain actually steps down

The most overlooked part of a fallback design is observability. I log every invocation with the depth at which it succeeded.

interface ModelInvocationLog {
  timestamp: string;
  intendedModel: string;
  actualModel: string;
  fallbackDepth: number;       // 0 = primary, 1 = first fallback, ...
  taskType: string;
  outcome: "success" | "all_failed";
}

I aggregate this weekly. If more than 5% of invocations land on fallbackDepth >= 1, that is the signal to revisit the configuration. The pattern I see is a 10–15% spike for the first one or two weeks after a new model release, settling back to roughly 2% afterward.

Reproducing it locally for testing

A small dev tip: this error only fires in production, so testing the fallback path is awkward. Sending a deliberately non-existent model id (for example claude-opus-99-xxx) returns a nearly identical response shape and is plenty good enough for a unit test that exercises the entire fallback chain. I run that test on every CI build now — ever since the one production incident where my fallback code "looked right" but I had never actually exercised the second step.

If you see this error, suspect your model id selection and your beta-feature combination first. If the issue lingers, invest in a real fallback design. After six months of running this in production, my off-hours incidents around new model rollouts went almost entirely silent — and that alone has been worth the effort.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API & SDK2026-06-28
A Silent Drop to a Weaker Model Is Scarier Than an Error: Designing a Capability Floor for Claude API Fallback
When a model becomes unavailable in an unattended pipeline, automatically dropping to a weaker model is dangerous. Drawing on years of running automated indie pipelines, this is how to use per-task capability contracts and a degradation budget to decide where to stop.
API & SDK2026-06-15
When a Model Disappears Without Warning: A State Machine for Retirement, Withdrawal, and Overload
A model can become unusable in hours for reasons that have nothing to do with a technical outage. This guide models three distinct flavors of 'unavailable'—retirement, withdrawal, and transient overload—as one availability state machine, with a router that keeps automated pipelines running. Working TypeScript and Python included.
API & SDK2026-05-29
Diagnosing invalid_request_error When You Pass an Image URL to the Claude API
When the Claude API rejects an image you passed via `source.type: url`, the root cause almost always lives in one of four buckets: scheme, MIME, size, or reachability. Here is the diagnostic order I use in production.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →