Skip to content

fix(config): include numeric bounds in schema validation errors#73220

Closed
alchip wants to merge 1 commit into
openclaw:mainfrom
alchip:fix/issue-52500-config-max-hint
Closed

fix(config): include numeric bounds in schema validation errors#73220
alchip wants to merge 1 commit into
openclaw:mainfrom
alchip:fix/issue-52500-config-max-hint

Conversation

@alchip

@alchip alchip commented Apr 28, 2026

Copy link
Copy Markdown

Summary

  • append explicit numeric bounds to mapped config validation messages for Zod too_big and too_small issues
  • this surfaces clear operator guidance like maximum allowed value is 5
  • add regression coverage for session.agentToAgent.maxPingPongTurns

Testing

  • pnpm vitest src/config/validation.allowed-values.test.ts

Fixes #52500

@greptile-apps

greptile-apps Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR appends explicit numeric bound hints (maximum allowed value is N / minimum allowed value is N) to Zod too_big/too_small validation messages, and adds a regression test for session.agentToAgent.maxPingPongTurns. The logic is correct for all existing config schemas, which exclusively use inclusive bounds (.min()/.max()).

Confidence Score: 4/5

Safe to merge; the only finding is latent and does not affect any current schema.

A single P2 finding — the missing inclusive check — keeps the score at 4. No current config schema uses exclusive bounds so there is no present runtime defect.

src/config/validation.ts — appendNumericConstraintHint should be updated to respect inclusive: false before any .lt()/.gt() bounds are added to config schemas.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/config/validation.ts
Line: 492-504

Comment:
**Missing `inclusive` check produces misleading bound hint**

Zod attaches an `inclusive` boolean to every `too_big` / `too_small` issue (`true` for `.max()`/`.min()`, `false` for `.lt()`/`.gt()`). When `inclusive` is `false`, the constraint is a strict inequality, so printing "maximum allowed value is N" is wrong — N itself is not allowed. All current config schemas happen to use inclusive bounds, so this is latent, but a future `.lt()` or `.gt()` addition would silently surface incorrect guidance.

```ts
if (record.code === "too_big") {
  const maximum = typeof record.maximum === "number" ? record.maximum : null;
  const inclusive = record.inclusive !== false; // default to inclusive if absent
  if (maximum !== null) {
    return inclusive
      ? `${message} (maximum allowed value is ${maximum})`
      : `${message} (value must be less than ${maximum})`;
  }
}

if (record.code === "too_small") {
  const minimum = typeof record.minimum === "number" ? record.minimum : null;
  const inclusive = record.inclusive !== false;
  if (minimum !== null) {
    return inclusive
      ? `${message} (minimum allowed value is ${minimum})`
      : `${message} (value must be greater than ${minimum})`;
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(config): append numeric bounds in va..." | Re-trigger Greptile

Comment thread src/config/validation.ts
Comment on lines +492 to +504
if (record.code === "too_big") {
const maximum = typeof record.maximum === "number" ? record.maximum : null;
if (maximum !== null) {
return `${message} (maximum allowed value is ${maximum})`;
}
}

if (record.code === "too_small") {
const minimum = typeof record.minimum === "number" ? record.minimum : null;
if (minimum !== null) {
return `${message} (minimum allowed value is ${minimum})`;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing inclusive check produces misleading bound hint

Zod attaches an inclusive boolean to every too_big / too_small issue (true for .max()/.min(), false for .lt()/.gt()). When inclusive is false, the constraint is a strict inequality, so printing "maximum allowed value is N" is wrong — N itself is not allowed. All current config schemas happen to use inclusive bounds, so this is latent, but a future .lt() or .gt() addition would silently surface incorrect guidance.

if (record.code === "too_big") {
  const maximum = typeof record.maximum === "number" ? record.maximum : null;
  const inclusive = record.inclusive !== false; // default to inclusive if absent
  if (maximum !== null) {
    return inclusive
      ? `${message} (maximum allowed value is ${maximum})`
      : `${message} (value must be less than ${maximum})`;
  }
}

if (record.code === "too_small") {
  const minimum = typeof record.minimum === "number" ? record.minimum : null;
  const inclusive = record.inclusive !== false;
  if (minimum !== null) {
    return inclusive
      ? `${message} (minimum allowed value is ${minimum})`
      : `${message} (value must be greater than ${minimum})`;
  }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/validation.ts
Line: 492-504

Comment:
**Missing `inclusive` check produces misleading bound hint**

Zod attaches an `inclusive` boolean to every `too_big` / `too_small` issue (`true` for `.max()`/`.min()`, `false` for `.lt()`/`.gt()`). When `inclusive` is `false`, the constraint is a strict inequality, so printing "maximum allowed value is N" is wrong — N itself is not allowed. All current config schemas happen to use inclusive bounds, so this is latent, but a future `.lt()` or `.gt()` addition would silently surface incorrect guidance.

```ts
if (record.code === "too_big") {
  const maximum = typeof record.maximum === "number" ? record.maximum : null;
  const inclusive = record.inclusive !== false; // default to inclusive if absent
  if (maximum !== null) {
    return inclusive
      ? `${message} (maximum allowed value is ${maximum})`
      : `${message} (value must be less than ${maximum})`;
  }
}

if (record.code === "too_small") {
  const minimum = typeof record.minimum === "number" ? record.minimum : null;
  const inclusive = record.inclusive !== false;
  if (minimum !== null) {
    return inclusive
      ? `${message} (minimum allowed value is ${minimum})`
      : `${message} (value must be greater than ${minimum})`;
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

@alchip

alchip commented Apr 29, 2026

Copy link
Copy Markdown
Author

Gentle bump 🙏\n\nCould a maintainer please help rerun the failed CI jobs for this PR? If rerun is green, happy to proceed toward merge. Thanks!

@alchip

alchip commented Apr 30, 2026

Copy link
Copy Markdown
Author

Rerun still failed on my side, sharing a quick summary for maintainer triage:\n\n- Run: https://github.com/openclaw/openclaw/actions/runs/25032385886\n- Failed job 1: → failed at step \n - Job: https://github.com/openclaw/openclaw/actions/runs/25032385886/job/73316744864\n- Failed job 2: → failed at step \n - Job: https://github.com/openclaw/openclaw/actions/runs/25032385886/job/73316932945\n\n@maintainer Could you please help investigate / rerun from maintainer side? Thank you 🙏

@alchip

alchip commented Apr 30, 2026

Copy link
Copy Markdown
Author

Correction (proper formatting): rerun is still failing; summary below for maintainer triage.

@maintainer Could you please help investigate / rerun from maintainer side? Thank you 🙏

@clawsweeper

clawsweeper Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge.

Summary
The PR adds numeric min/max hints to mapped Zod config validation errors and a regression test for session.agentToAgent.maxPingPongTurns.

Reproducibility: yes. Source inspection shows session.agentToAgent.maxPingPongTurns: 10 reaches a .max(5) Zod schema while current main returns the raw Zod message through mapZodIssueToConfigIssue without a maximum-bound hint.

Next step before merge
The remaining work is narrow and mechanical on an open contributor PR: correct one helper, extend focused tests, add the changelog line, and rerun validation.

Security
Cleared: The diff only changes config validation message formatting and a focused test, with no dependency, workflow, credential, permission, install, build, or publishing surface touched.

Review findings

  • [P2] Restrict numeric hints to numeric issues — src/config/validation.ts:492-509
  • [P2] Respect exclusive numeric bounds — src/config/validation.ts:492-509
  • [P3] Add the changelog entry — src/config/validation.ts:518
Review details

Best possible solution:

Land a narrow mapper change that enriches only numeric-origin bounds, handles inclusive and exclusive constraints accurately, preserves allowed-values behavior, adds focused regression coverage, and records the diagnostics fix in CHANGELOG.md.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows session.agentToAgent.maxPingPongTurns: 10 reaches a .max(5) Zod schema while current main returns the raw Zod message through mapZodIssueToConfigIssue without a maximum-bound hint.

Is this the best way to solve the issue?

No, not as submitted. mapZodIssueToConfigIssue is the right surface, but the helper must restrict hints to numeric origins, respect inclusive === false, and include the changelog entry before merge.

Full review comments:

  • [P2] Restrict numeric hints to numeric issues — src/config/validation.ts:492-509
    Zod emits too_big and too_small with numeric bounds for string length and array/set/file size checks too. Since this helper only checks the issue code and bound value, existing config string/collection constraints could get wording like minimum allowed value is 1; gate the hint on numeric origins such as number or int.
    Confidence: 0.9
  • [P2] Respect exclusive numeric bounds — src/config/validation.ts:492-509
    Zod reports .gt()/.lt() failures with inclusive: false, and current config schemas use .positive(), which maps to .gt(0). As written, rejected zero values would be described with minimum allowed value is 0, making the boundary look valid; branch on record.inclusive === false and cover it in tests.
    Confidence: 0.9
  • [P3] Add the changelog entry — src/config/validation.ts:518
    This changes user-facing config validation diagnostics, but the PR does not update CHANGELOG.md. Add a single-line Unreleased Fixes entry with appropriate contributor credit so the release notes include the diagnostics fix.
    Confidence: 0.87

Overall correctness: patch is incorrect
Overall confidence: 0.89

Acceptance criteria:

  • pnpm test src/config/validation.allowed-values.test.ts
  • pnpm exec oxfmt --check --threads=1 src/config/validation.ts src/config/validation.allowed-values.test.ts CHANGELOG.md
  • git diff --check
  • pnpm check:changed in Testbox or the maintainer default changed-gate lane before merge

What I checked:

  • current-main-mapper-lacks-bound-hints: mapZodIssueToConfigIssue currently maps Zod issue messages and allowed-values metadata only; current main has no numeric-bound enrichment for too_big or too_small. (src/config/validation.ts:492, 23e0be355ac3)
  • reported-field-has-bounded-schema-and-docs: session.agentToAgent.maxPingPongTurns is constrained with .min(0).max(5), and the public config docs describe the integer range as 0-5. (src/config/zod-schema.session.ts:66, 23e0be355ac3)
  • pr-helper-checks-code-and-bound-only: The PR adds appendNumericConstraintHint, but the helper only checks the issue code plus numeric maximum/minimum, so it does not distinguish numeric origins from string/array/file size origins and does not branch on inclusive. (src/config/validation.ts:492, 01fcc4b512d5)
  • zod-issue-contract-is-broader-than-numbers: Zod v4.4.1 too_big/too_small issues include origins such as string, array, set, and file; max/min length and size checks emit numeric maximum/minimum values.
  • exclusive-bounds-exist-in-current-configs: Zod v4.4.1 .gt()/.lt() set inclusive: false, and .positive() maps to .gt(0); current config schemas use many positive numeric fields, so the PR would describe rejected zero values as if 0 were allowed. (src/config/zod-schema.session.ts:21, 23e0be355ac3)
  • changelog-entry-missing: The active Unreleased Fixes section exists, but CHANGELOG.md has no entry for fix: config schema ceiling rejections should explicitly state the maximum allowed value #52500 or the maximum-bound validation-message fix. (CHANGELOG.md:14, 23e0be355ac3)

Likely related people:

  • Jason: Recent current-main history for src/config/validation.ts shows Jason touching the central validation mapper file in commit 53bd718. (role: recent maintainer; confidence: medium; commits: 53bd718a1ac5; files: src/config/validation.ts)
  • @steipete: The current maxPingPongTurns schema line blames to commit afe42fc, whose subject credits @steipete; that field is the concrete reproduction path for the linked issue. (role: adjacent field maintainer; confidence: medium; commits: afe42fc9774a; files: src/config/zod-schema.session.ts, docs/gateway/config-agents.md)

Remaining risk / open question:

  • The PR would add value-bound wording to Zod string length and collection/file size validation errors unless the helper gates on numeric issue origins.
  • The PR would misstate exclusive numeric bounds such as .positive()/.gt(0) by saying the rejected boundary is an allowed minimum.
  • The user-facing diagnostics change is missing the required CHANGELOG.md entry.
  • The PR discussion reports failing CI reruns, so validation should be rerun after the focused patch is corrected.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 23e0be355ac3.

@alchip

alchip commented May 11, 2026

Copy link
Copy Markdown
Author

Friendly bump 🙏 Could a maintainer help take a look when time permits? Happy to adjust anything needed.

@alchip

alchip commented May 11, 2026

Copy link
Copy Markdown
Author

Closing this for now to reduce queue noise; can reopen or resubmit if needed.

@alchip alchip closed this May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: config schema ceiling rejections should explicitly state the maximum allowed value

1 participant