fix: normalize openclaw agent.model object form before .startsWith (#1216)

OpenClaw 2026.5.x writes agents[].model as either a plain string
or as an object { primary, fallbacks }. The status enrichment and
POST cleanup both called .startsWith() on the raw value, which threw
TypeError when the object form was present and made the dashboard
report 'not configured'.

Add a resolveAgentModel helper that accepts both shapes and returns
the string id (model.primary for the object form, empty string for
missing/invalid). Use it when enriching agents in GET so consumers
receive a string model field, and when filtering the list in POST.

Refs decolua/9router#1196
This commit is contained in:
Matt Van Horn
2026-05-17 01:14:31 -07:00
committed by GitHub
parent e03b28138a
commit a168313290

View File

@@ -9,6 +9,15 @@ import os from "os";
const execAsync = promisify(exec);
// OpenClaw 2026.5.x writes agents[].model as either a plain string
// (legacy) or as an object `{ primary, fallbacks }`. Normalize to the
// string id so downstream consumers can call `.startsWith()` safely.
const resolveAgentModel = (m) => {
if (typeof m === "string") return m;
if (m && typeof m === "object") return m.primary ?? "";
return "";
};
const getOpenClawDir = () => path.join(os.homedir(), ".openclaw");
const getOpenClawSettingsPath = () => path.join(getOpenClawDir(), "openclaw.json");
@@ -79,12 +88,14 @@ export async function GET() {
const settings = await readSettings();
// Enrich agents list with current per-agent model from models.json
// Enrich agents list with current per-agent model from models.json.
// Coerce agent.model to its string id when OpenClaw stores it as
// `{ primary, fallbacks }` so downstream `.startsWith()` calls work.
const agentList = settings?.agents?.list || [];
const enrichedAgents = await Promise.all(
agentList.map(async (agent) => {
const agentModel = agent.agentDir ? await readAgentModel(agent.agentDir) : null;
return { ...agent, currentModel: agentModel };
return { ...agent, model: resolveAgentModel(agent.model), currentModel: agentModel };
})
);
@@ -169,10 +180,11 @@ export async function POST(request) {
settings.agents.defaults.models[`9router/${m}`] = {};
});
// Remove old 9router model from each agent in agents.list
// Remove old 9router model from each agent in agents.list. The
// model field may be a plain string or `{ primary, fallbacks }`.
if (settings.agents.list) {
settings.agents.list = settings.agents.list.map((agent) => {
if (agent.model?.startsWith("9router/")) {
if (resolveAgentModel(agent.model).startsWith("9router/")) {
const { model: _, ...rest } = agent;
return rest;
}