Skip to content

fix(cron): strip internal whitespace from model IDs in cron job normalization#2502

Open
BingqingLyu wants to merge 2 commits into
mainfrom
fork-pr-66543-fix-cron-model-id-whitespace
Open

fix(cron): strip internal whitespace from model IDs in cron job normalization#2502
BingqingLyu wants to merge 2 commits into
mainfrom
fork-pr-66543-fix-cron-model-id-whitespace

Conversation

@BingqingLyu

@BingqingLyu BingqingLyu commented Apr 28, 2026

Copy link
Copy Markdown
Owner

Summary

  • Problem: Cron tool preserves internal whitespace in model IDs (e.g. custom-1/ minimax/ minimax-m2.5-free), causing OpenRouter to reject them with 400 ... is not a valid model ID (format).
  • Why it matters: All cron jobs using affected model IDs fail silently, and users cannot fix them via update/delete due to the same corruption path.
  • What changed: coercePayload() and copyTopLevelAgentTurnFields() in src/cron/normalize.ts now strip all internal whitespace from model and fallbacks fields via .replace(/\s+/g, '').
  • What did NOT change (scope boundary): No changes to store serialization, schedule normalization, delivery logic, or any other payload fields.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: TrimmedNonEmptyStringFieldSchema only .trim()s leading/trailing whitespace. LLMs frequently insert spaces around / and - separators when generating tool call parameters. The normalization layer had no guard against internal whitespace in model IDs.
  • Missing detection / guardrail: No validation that model IDs are whitespace-free after trimming.
  • Contributing context (if known): LLM-generated tool call parameters are the input source; they routinely break on long slash-delimited identifiers.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/cron/normalize.test.ts
  • Scenario the test should lock in: normalizeCronJobCreate with model "custom-1/ minimax/ minimax- m2.5- free" and fallbacks containing internal spaces should output clean IDs with zero whitespace.
  • Why this is the smallest reliable guardrail: The corruption happens in coercePayload during normalization — a unit test on normalizeCronJobCreate catches it at the exact mutation point.
  • Existing test that already covers this (if any): Existing test only covered leading/trailing trim, not internal whitespace.
  • If no new test is added, why not: New test added.

User-visible / Behavior Changes

Model IDs with accidental internal whitespace are now silently corrected instead of being persisted as-is. No config changes.

Diagram (if applicable)

Before:
[LLM sends "custom-1/ minimax/ minimax-m2.5-free"] -> [coercePayload .trim()] -> [stored with spaces] -> [OpenRouter 400]

After:
[LLM sends "custom-1/ minimax/ minimax-m2.5-free"] -> [coercePayload .trim() + .replace(/\s+/g, '')] -> [stored clean] -> [OpenRouter OK]

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Windows 11 / Ubuntu 24 (reported)
  • Runtime/container: Node.js / Docker
  • Model/provider: OpenRouter → MiniMax
  • Integration/channel (if any): Cron → isolated session
  • Relevant config (redacted): custom-1/minimax/minimax-m2.5-free

Steps

  1. Create cron job with model custom-1/minimax/minimax-m2.5-free
  2. LLM inserts spaces: custom-1/ minimax/ minimax- m2.5- free
  3. Job fails with 400 invalid model ID

Expected

  • Model ID stored as custom-1/minimax/minimax-m2.5-free

Actual

  • Model ID stored as custom-1/ minimax/ minimax- m2.5- free

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

53/53 tests pass including new "strips internal whitespace from model IDs" test.

Human Verification (required)

  • Verified scenarios: normalizeCronJobCreate with internal spaces in model and fallbacks fields
  • Edge cases checked: model with only leading/trailing spaces (existing test), model with multiple consecutive internal spaces, fallbacks array with spaces
  • What you did not verify: Live cron execution on a running OpenClaw instance

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: Model IDs that intentionally contain spaces would be broken.
    • Mitigation: No known provider uses spaces in model IDs. OpenRouter, Anthropic, Google, and OpenAI all use / and - delimiters without spaces.

@BingqingLyu BingqingLyu added conflicting-group-1 Conflicting PR group 1 — review as a batch conflicting-pr Shares at least one cross-PR dependency with other PRs labels May 26, 2026
@BingqingLyu

Copy link
Copy Markdown
Owner Author

⚠️ Cross-PR Conflict Detected

This PR shares modified code with #2127, #2245, #2444.

Shared functions:

Function File Also modified by
coercePayload src/cron/normalize.ts #2127, #2245
normalizeMainSystemEventCreateJob src/cron/normalize.test.ts #2127, #2245, #2444

Recommendation: Coordinate with #2127, #2245, #2444 before merging.


Auto-detected by codegraph — a code graph analysis tool built on neug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicting-group-1 Conflicting PR group 1 — review as a batch conflicting-pr Shares at least one cross-PR dependency with other PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Cron Tool Corrupts Model IDs

2 participants