openclaw - ✅(Solved) Fix `openclaw status` does an unsafe `.trim()` on `runtimeModel` — crashes if any session entry has a non-string `model`/`modelProvider`/`providerOverride`/`modelOverride` [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#76206Fetched 2026-05-03 04:40:46
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
2
Timeline (top)
commented ×1cross-referenced ×1referenced ×1

Fix Action

Fix / Workaround

Workaround applied locally (2026-04-26): VOSS normalized the bad claude-local session entry. As of 2026-05-02 all 12 agent sessions.json files are clean (verified via jq), so the crash does not currently reproduce here — but the unsafe code path remains in 2026.4.29.

PR fix notes

PR #76216: fix(status): guard resolveSessionModelRef against non-string model fields (#76206)

Description (problem / solution / changelog)

Problem

openclaw status crashes with TypeError: runtimeModel?.trim is not a function when any session entry in ~/.openclaw/agents/<agent>/sessions/sessions.json has a non-string value for model, modelProvider, providerOverride, or modelOverride.

Root cause: readSessionStoreReadOnly parses session JSON with z.record(z.string(), z.unknown()) — no field normalization. The four model fields reach resolvePersistedSelectedModelRef typed as string | undefined but holding arbitrary JSON values. The internal .trim() calls crash on objects or numbers.

The loadSessionStore path does normalize via normalizeSessionRuntimeModelFields, but openclaw status uses readSessionStoreReadOnly for its read-only scan.

Fix

Wrap the four fields with normalizeOptionalString() at the resolveSessionModelRef call site in status.summary.runtime.ts. normalizeOptionalString accepts unknown and returns string | undefined, so non-string values are discarded and the resolver falls back to the configured default.

Tests

Two regression tests added to status.summary.runtime.test.ts:

  • object model field → does not throw, falls back to configured default
  • object modelOverride + number providerOverride → does not throw

Fixes #76206

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/commands/status.summary.runtime.test.ts (modified, +17/-0)
  • src/commands/status.summary.runtime.ts (modified, +4/-4)

Code Example

const runtimeModel = params.runtimeModel?.trim();

---

function resolveSessionModelRef(cfg, entry, agentId) {}
    runtimeProvider: entry?.modelProvider,
    runtimeModel: entry?.model,
    overrideProvider: entry?.providerOverride,
    overrideModel: entry?.modelOverride,

---

const trimString = (v: unknown): string | undefined =>
  typeof v === "string" ? v.trim() || undefined : undefined;
RAW_BUFFERClick to expand / collapse

Version: confirmed in 2026.4.29 (active install at /opt/homebrew/lib/node_modules/openclaw/dist/); same pattern present in 2026.4.11 (npm-installed copy at ~/.nvm/.../openclaw/dist/). First observed 2026-04-26 with a stale claude-local session entry that had a non-string model.

Stack location:

  • Crash site (display path): dist/model-selection-display-CZKOmf9u.js:3 (and lines 4, 11, 12, 19) —

    const runtimeModel = params.runtimeModel?.trim();

    The ?. only protects against null/undefined. If runtimeModel is e.g. an object ({provider, model, baseUrl}), .trim is undefinedTypeError: runtimeModel?.trim is not a function.

  • Same bug also at dist/model-selection-sqz--Abu.js:52 (resolver path; resolvePersistedModelRef).

  • Caller: dist/commands/status.summary.runtime.js:75-89

    function resolveSessionModelRef(cfg, entry, agentId) {}
      runtimeProvider: entry?.modelProvider,
      runtimeModel: entry?.model,
      overrideProvider: entry?.providerOverride,
      overrideModel: entry?.modelOverride,

    These are passed unchecked from ~/.openclaw/agents/<agent>/sessions/sessions.json entries.

Repro: put any non-string value (object, number) in model/modelProvider/providerOverride/modelOverride of any session entry in any agent's sessions.json, then run openclaw status. (We did not corrupt live state to confirm — diagnosis is from reading the dist code; the historical crash on 2026-04-26 matched the same path.)

Suggested fix: replace the unsafe ?.trim() calls with a string-narrowing helper. Apply at minimum to:

  • model-selection-display.tsruntimeModel, runtimeProvider, overrideModel, overrideProvider, fallbackModel
  • model-selection.tsruntimeModel, runtimeProvider, overrideModel, overrideProvider in resolvePersistedModelRef (and defaultProvider.trim() should also tolerate undefined).
const trimString = (v: unknown): string | undefined =>
  typeof v === "string" ? v.trim() || undefined : undefined;

Why it matters:

  • A single bad session entry takes down the entire openclaw status CLI — the primary diagnostic surface — for all agents.
  • The data shape that triggers it is reachable from upstream code paths (pi-embedded-runner writes runtime model objects in some flows); a downstream serialization or session-rotation bug can leave entries in this shape, and then status is unusable until someone manually edits the JSON.
  • Defense-in-depth: status should never crash. It's the tool you reach for when something else is broken.

Workaround applied locally (2026-04-26): VOSS normalized the bad claude-local session entry. As of 2026-05-02 all 12 agent sessions.json files are clean (verified via jq), so the crash does not currently reproduce here — but the unsafe code path remains in 2026.4.29.

extent analysis

TL;DR

Replace unsafe ?.trim() calls with a string-narrowing helper to prevent TypeErrors when non-string values are encountered.

Guidance

  • Identify all locations where ?.trim() is used on potentially non-string values, such as runtimeModel, runtimeProvider, overrideModel, overrideProvider, and fallbackModel.
  • Implement a string-narrowing helper function, like trimString, to safely handle non-string values.
  • Apply this helper function to all identified locations, including model-selection-display.ts and model-selection.ts.
  • Verify that the fix works by testing with non-string values in session entries and running openclaw status.

Example

const trimString = (v: unknown): string | undefined =>
  typeof v === "string" ? v.trim() || undefined : undefined;

This helper function can be used to replace ?.trim() calls, ensuring that only string values are trimmed.

Notes

The current workaround of manually editing JSON files is not a reliable solution, as the issue can recur if non-string values are written to session entries again. A code fix is necessary to prevent crashes and ensure the stability of the openclaw status CLI.

Recommendation

Apply the suggested fix by replacing ?.trim() calls with the trimString helper function to prevent TypeErrors and ensure the stability of the openclaw status CLI. This fix is necessary to prevent crashes and provide a reliable diagnostic surface for all agents.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix `openclaw status` does an unsafe `.trim()` on `runtimeModel` — crashes if any session entry has a non-string `model`/`modelProvider`/`providerOverride`/`modelOverride` [1 pull requests, 1 comments, 2 participants]