Description
Problem
ModelAlias.resolveRef throws AliasMaxDepthError for an alias chain that successfully settles exactly on the depth limit. The post-loop check cannot distinguish "the loop settled and broke out" from "the loop ran out of depth", so a legitimate chain one hop shorter than the apparent limit already fails.
Evidence (verified 2026-10-24 on main 58dd3fb)
File: packages/schema/src/model-alias.ts
MAX_DEPTH = 10(line 32); loop iswhile (depth < MAX_DEPTH)(line 61), incrementingdepthat the top of every iteration — including the final iteration that merely confirms the settledprovider/modelandbreaks.- After the loop, line 153 unconditionally throws when
depth >= MAX_DEPTH, even when the loop exited viabreakwith a fully resolved reference.
Reproduction (Bun, importing packages/core/src/model-alias.ts; global model aliases a0 -> a1 -> ... -> a(n-1) -> openai/gpt-4o, i.e. n expansion hops):
| Hops | Result |
|---|---|
| 5 | "openai/gpt-4o" |
| 8 | "openai/gpt-4o" |
| 9 | THROWS AliasMaxDepthError: Max alias resolution depth exceeded for: a0 |
| 10, 11 | THROWS AliasMaxDepthError (expected for 10+; the 9-hop case is the defect) |
A 9-hop chain needs 9 expansion iterations plus 1 settling iteration = depth 10, hits the post-loop check, and is rejected despite having resolved. The effective usable limit is therefore 8 hops, not the documented-by-constant 10, and the error message misreports a resolved chain as a runaway one.
Why it matters
Alias chains are how users layer naming (team alias -> provider alias -> concrete model). A chain that is within the intended limit fails with an error that claims the opposite, and the failure boundary (8 usable hops) matches neither the constant nor any documented limit, so users cannot predict whether a config will work without counting loop iterations including the invisible settling pass.
Acceptance criteria
- A chain that settles within
MAX_DEPTHtotal loop iterations resolves instead of throwing — in particular the verified 9-hop chain above returnsopenai/gpt-4o. - A chain that genuinely never settles within the limit (cycle-free but longer than the limit, and cycles longer than the visited-set catches) still throws
AliasMaxDepthError; cycle detection (AliasCycleError) is unchanged. - The fix distinguishes settled-vs-exhausted loop exit (e.g. track settlement explicitly) rather than just raising
MAX_DEPTH, so the boundary stays exactly at the constant. - New tests in
packages/core/test/model-alias.test.tscover the boundary: the largest chain that should resolve, and the smallest that should throw. Existing tests (including test 13, chained expansion) still pass. - Dedup checked 2026-10-24: no open or closed issue mentions
AliasMaxDepth/ max-depth alias behaviour.
Single-repo: ModelAlias lives only in this repo's schema/core packages.
Activity
dezren39 commentedon Oct 24, 2026
Fixed by PR #403 (merged, squash bca2f2e), which carried the #398 and #399 fixes as one set — GitHub auto-closed only #398 from the
Fixes #398 and #399line, so closing this manually. The fix tracks loop settlement explicitly inpackages/schema/src/model-alias.tsand throwsAliasMaxDepthErroronly on an unsettled exit; boundary verified by new tests 18/19 inpackages/core/test/model-alias.test.ts(9-hop chain resolves, 10-hop throws; suite 19/19 pass).docs: record the #397, #389 and #398/#399 completions in ROADMAP Done (…