Skip to content

pi: alias 'fable' resolves to a non-existent model id and is silently substituted #6577

Description

@waynesun09

Rewritten after review. The original framing called the pinned aliases a "silent downgrade".
That was wrong: on Vertex, model access is per-project, so pinning aliases to specific ids that
are known-enabled is deliberate and safer than resolving to whatever is newest. The real bug is
narrower — see below.

Summary

fable is not in piModelAliases, so on pi it falls through the bare-id path to
anthropic-vertex/fable — a model no provider registers. pi does not error: it substitutes a
fallback model with the wrong wire id and only warns, and that warning is invisible in a normal run.

The bug

var piModelAliases = map[string]string{
	"opus":   "claude-opus-4-6",
	"sonnet": "claude-sonnet-4-6",
	"haiku":  "claude-haiku-4-5",
}

Probed through translatePiModel on main:

harness model: resolves to registered?
opus anthropic-vertex/claude-opus-4-6 yes
sonnet anthropic-vertex/claude-sonnet-4-6 yes
haiku anthropic-vertex/claude-haiku-4-5 yes
fable anthropic-vertex/fable no

fable is documented as a valid alias in docs/runtimes/claude.md and docs/runtimes.md, and
Claude Code accepts it natively — so a harness that works on claude breaks on pi, and breaks
quietly.

What happens on the pi side: resolveCliModel finds no match and calls buildFallbackModel, which
clones a real model but replaces its id with the unresolvable string, returns error: undefined and
only a warning. buildPiRunCommand sends pi's stderr to pi-debug.log only when --debug is
set, so in a normal run nothing surfaces. This is the same silent-substitution class the
xai/grok-4.6 normalisation was added to close in #6571.

Suggested fix

  • Add fable to the alias table, mapped to a model id enabled in the fleet's Vertex project.
  • Fail loudly on an unmapped alias. Anything in a known alias vocabulary with no mapping should
    be a startup error naming the alias, rather than a bare id that cannot resolve and gets silently
    substituted. This is the part worth fixing regardless of which ids the table holds.
  • Consider a test asserting every alias in the table resolves to a model the pinned pi version
    actually registers — catching a typo or a removed id locally rather than in a live run. It should
    assert registration, not recency: pinning to older, known-enabled ids is intentional.

Not a bug: the pinned ids

opus and sonnet map to 4-6 while pi's catalog also lists claude-opus-5 / claude-sonnet-5.
That is deliberate. Vertex enables models per project, so the newest id in pi's catalog is not
necessarily accessible to a given project, and resolving an alias to "latest" would break runs in
projects that do not have it. Claude Code resolves aliases natively to the current tier model, which
carries that same risk on Vertex — worth knowing, but not something to fix here.

Docs follow-on

docs/runtimes/claude.md says aliases resolve to "the current Anthropic model of that tier". On
Vertex that is not guaranteed to be accessible — per-project enablement decides. Both runtime pages
should say that model availability is per-project on Vertex, and that pi's alias table is a
fullsend-owned pinned mapping rather than a passthrough.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugcomponent/runnerAgent runner behavior and lifecyclegoPull requests that update go codepriority/mediumNormal priority, plan for next cycleready-to-codeTriggers code agent dispatchtype/bugConfirmed defect in existing behavior

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions