Repository navigation
[BUG] Studio Models panel silently no-ops when agent model is a function returning a fallback array #27088
Description
Describe the Bug
Studio's Models panel (shown in the agent Config side panel when an agent is configured with a fallback array) lets you reorder entries, change the provider/model of an entry, and enable/disable entries. All of this works when model is a static array, but when model is a function that returns an array every action is a silent no-op.
Agent.getModelList() resolves the function and returns the list, so the server includes modelList in GET /api/agents/:agentId and Studio renders the panel. But Agent.reorderModels() and Agent.updateModelInModelList() (packages/core/src/agent/agent.ts) start with if (!Array.isArray(this.model)), log Model is not an array, and return. The server handlers for POST /agents/:agentId/models/reorder and POST /agents/:agentId/models/:modelConfigId still respond 200 { message: 'Model list reordered' } / { message: 'Model updated in model list' }, so the UI shows success and nothing changed.
A function-valued model array is the documented way to build the chain from runtime values (environment variables, request context, provider keys), so this is a common configuration for deployed agents.
Related gaps in the same panel, worth fixing alongside:
- There is no control for a per-entry
maxRetries, even though the server route accepts it and the response includes it. AgentMetadataModelList(packages/playground/src/domains/agents/components/agent-metadata/agent-metadata-model-list.tsx) seeds its list and per-itemenabledstate once withuseState(() => modelList), so a refetch of the agent does not update what the panel shows.- The panel is not documented. Model fallbacks are only described at the code level in
docs/src/content/en/models/index.mdx; nothing underdocs/studiomentions the panel, reordering, or enable/disable.
Note: changes made in this panel are intentionally in-memory only today and revert on restart. Making them persistent is tracked separately as a stacked feature request (linked below once filed). This issue is only about the panel working as designed.
Steps To Reproduce
- Configure an agent with a function returning a fallback array:
import { Agent } from '@mastra/core/agent';
import type { ModelWithRetries } from '@mastra/core/agent';
function buildChain(): ModelWithRetries[] {
return [
{ id: 'primary', model: process.env.PRIMARY_MODEL ?? 'anthropic/claude-sonnet-4-5', maxRetries: 2 },
{ id: 'fallback', model: 'anthropic/claude-haiku-4-5', maxRetries: 1 },
];
}
export const agent = new Agent({
id: 'fallback-agent',
name: 'Fallback Agent',
instructions: 'You are a helpful assistant.',
model: () => buildChain(),
});- Run
mastra dev, open the agent in Studio, open the Config side panel. The Models section appears with both entries. - Drag
fallbackaboveprimary, or togglefallbackoff, or change its model. - Observe the request returns 200 and the server logs
Model is not an array. Refresh the page: the list is unchanged. - Replace
model: () => buildChain()withmodel: buildChain()and repeat. Now the actions take effect (until restart).
Link to Minimal Reproducible Example
Snippet above; happy to turn it into a repo if needed.
Expected Behavior
Either:
- Reorder / update / enable-disable work for function-valued model arrays the same way they do for static arrays (for example by resolving the function once and replacing
this.modelwith the mutated resolved array, the same way__updateModelreplaces the whole value), or - the server returns an error and Studio renders the panel read-only (or hides the mutators) when the model is dynamic, instead of reporting success.
Plus: a maxRetries control in the panel, panel state that follows refetches, and a short docs section under docs/studio describing the Models panel and that its changes are in-memory only.
Environment Information
@mastra/core1.78.0-alpha.2 (main at d99fa30), also reproduces on recent stable releases- Node 22, macOS
- Studio via
mastra dev
Verification
- I have searched the existing issues to make sure this is not a duplicate
- I have included sufficient information for the team to reproduce and understand the issue
Activity
dane-ai-mastra commentedon Oct 14, 2026
Thank you for opening this issue. It is waiting for maintainer triage or approval.
Please wait until maintainers remove the triage or approval label before opening a pull request.
mikhael28 commentedon Oct 14, 2026
Stacked follow-up: #27089 (persist model fallback chain edits made in Studio) builds on this fix.
mastra-platform commentedon Oct 14, 2026
modelis a function returning an arraymain(04b4775)modelinreorderModels/updateModelInModelListUnderstanding
GET /api/agents/:agentIdcallsagent.getModelList(). That method resolves a function-valuedmodel(packages/core/src/agent/agent.ts~3836), so the panel renders.POST .../models/reorderandPOST .../models/:modelConfigId(packages/server/src/server/handlers/agents.ts~3589–3660) first checkgetModelList(), which passes. They then call the sync methodsreorderModels/updateModelInModelList. Both return early when!Array.isArray(this.model)(agent.ts:3950–3952,:3968–3980) and only logModel is not an array. The route still returns 200.agent-metadata-model-list.tsxseedsuseStateonce and updates optimistically, so the panel looks updated until you reload.Fix direction: make both methods async. Resolve the function once (the same way
resolveModelSelection/normalizeModelFallbacksdo), replacethis.modelwith the resolved array, then apply the change.awaitthe methods in the handlers. Return a non-2xx status if the change can't be applied. Add a function-valued case topackages/core/src/agent/__tests__/model-list.test.ts.Related: #27089 (persistence, a separate follow-up), #13410 / #13445 (reorder sorting), #9481 (model reset). No open or merged PR fixes this yet.
Assumptions
requestContext.maxRetriescontrol, refetch syncing, and Studio docs the reporter asked for are follow-ups, not required for this fix.main, so the verdict is unchanged.Open questions
requestContext, is it acceptable to store one resolved result? Or should Studio edits on dynamic models be rejected with an explicit error and the panel shown read-only?Reproduction
Not run live. Static trace on current source. A deterministic repro:
new Agent({ model: () => [{ model: a }, { model: b }] })→await getModelList()returns 2 entries →reorderModels([...ids].reverse())logsModel is not an arrayand the list is unchanged.