Managed agent runtimes pin your compute to a region. On Vertex AI’s Agent Engine, the reasoning engine runs wherever you deployed it, and that deploy region becomes the runtime’s ambient GOOGLE_CLOUD_LOCATION; the default location for every downstream model call. That’s fine when every model you serve is available there. It stops being fine the moment you add Anthropic Claude.
Claude on Vertex is served from a small set of regions plus the us/eu/global multi-region endpoints; not from every region that hosts an agent runtime. The availability matrix for a hosted third-party model rarely lines up with wherever your orchestration happens to live. We hit this on the first real deploy of a multi-model agent (Gemini + Claude on one Vertex substrate): Gemini answered, Claude 404’d. The engine’s home region simply didn’t serve Claude.
The obvious fix, tell the model call which region to use, collides with how agent frameworks resolve models. Google ADK’s registry resolves a model by regex fullmatch on the model string: claude-sonnet-4-6 matches a claude-.* pattern and resolves to the Claude class, which then reads the ambient GOOGLE_CLOUD_LOCATION for its region. To override the region you pass a full resource path, projects/<id>/locations/<region>/publishers/anthropic/models/<name>, because the class extracts project and region straight from that string instead of the env var. But a projects/... path never matches claude-.*, so the registry can’t resolve it from a string at all.
The way through: return an actual model object, not a string. LlmAgent.canonical_model short-circuits to isinstance(model, BaseLlm) before it ever consults the registry, so a constructed Claude(model=<resource_path>) carries its own region and bypasses regex resolution entirely. The discipline is to do this only when you have to:
CLAUDE_SERVING = frozenset(
{"global", "us", "eu", "us-east5", "europe-west1", "asia-southeast1"}
)
def resolve_model(model_id: str) -> str | Claude:
if not model_id.startswith("claude-"):
return model_id # let the registry resolve by name
ambient = os.getenv("GOOGLE_CLOUD_LOCATION", "").strip().lower()
if ambient in CLAUDE_SERVING:
return model_id # ambient already serves it; respect it
project = os.getenv("GOOGLE_CLOUD_PROJECT", "").strip()
if not project:
return model_id # no project (tests); nothing to pin
# A projects/.../locations/... path never matches "claude-.*", so a string
# can't carry it through the registry. Return an object it won't re-resolve.
loc = os.getenv("CLAUDE_VERTEX_LOCATION", "global")
path = f"projects/{project}/locations/{loc}/publishers/anthropic/models/{model_id}"
return Claude(model=path)
When the ambient region already serves Claude, local dev pointed at global, say, return the bare ID and let the registry resolve it normally. Overriding unconditionally means every environment fights the registry, and you clobber an operator’s deliberate region choice. The pin is an escape hatch gated on “the ambient region can’t serve this model,” not a blanket rewrite.
One more lesson, learned the expensive way. The first version pinned to a single serving region. Under load that one region intermittently stalled; the LLM call went out, no response, no error, an empty stream; a retry of the same prompt succeeded. Blank, hung turns with nothing in the logs. The fix was to point the pin at the global endpoint instead of a single region, letting Vertex load-balance across Claude-serving regions rather than concentrating traffic on one. A single region’s capacity is a single point of failure; a multi-region or global endpoint isn’t.
Two things generalize past Vertex and Claude. Multi-model serving on any cloud AI platform has a regional availability matrix, and it will not match your compute’s home region, check it per model, not per platform, and pin deliberately. And make the pin conditional: a registry that resolves by name is doing useful work everywhere the defaults are right, so step around it only where they’re wrong, using the mechanism the framework already hands you, an object it won’t try to re-resolve; rather than one it will fight.