Repository navigation
Description
Problem
A field whose schema declares a default can have the edit in progress reverted: text that cannot be sent reports no value, which resolves straight back to the default, and the field's draft is rewritten to it.
Reproducible today — number fields. With n: { "type": "integer", "default": 30 }, clearing the box refills it with 30. The argument cannot be emptied.
Latent — the JSON editor. The same mechanism applies to an array or object argument on the JSON fallback editor, and there it is worse (almost every edit passes through an invalid intermediate state, so the field would be uneditable). It does not reproduce through today's seeding path: collectSchemaDefaults seeds the value with the same object reference as the schema default, so the substitution returns an identical reference and the re-sync never fires. It becomes reachable the moment the field's value is a structurally-equal but distinct object — values arriving parsed from the wire or from a deep link, or a nested-object default (collectSchemaDefaults builds a fresh object for that case).
Numbers have no such protection because they are primitives: 3 → 30 is a real change.
Mechanism
Two pieces of clients/web/src/components/groups/SchemaForm/SchemaForm.tsx combine:
resolveValue()substitutes the schemadefaultwhenevervalues[fieldName]isundefined.- The two draft-holding fields re-sync their text when the parent's value diverges from what the draft parses to (
SchemaNumberInput,SchemaJsonField).
A draft that cannot be sent reports undefined by design — an inspector must not report a value it cannot send. That undefined then resolves back to the default, which is a change from the previous value, so the re-sync rewrites the user's draft to the default.
It settles after one revert — from then on the value is pinned at the default and stops changing — but the keystroke is already lost.
Reproduction (number field)
Tool argument n: { "type": "integer", "default": 30 }:
- Open the tool. The field shows
30. - Clear the box.
- It refills with
30.
Expected
The text being typed survives. The schema default is what the field opens with, not a value re-imposed on every unsendable draft.
Notes
Found while verifying #1853 / #1856 / #1885 and noted as out of scope in #2025. It is the same "the editor fights you" family as those three, with reversion rather than escaping as the symptom, and it is not covered by their fixes.
Sending the default is usually equivalent to omitting the argument for the tools these schemas come from, but default is a JSON Schema annotation and a server is not obliged to apply it, so the two are not guaranteed equivalent.
Edited after capturing the before/after screenshots: the original write-up claimed the array case reproduced in the app. It does not, for the reference-identity reason above; the number case does. The fix covers both, since the JSON half is one distinct object away from being live.
Activity
cliffhall commentedon Aug 16, 2026
Triage: Priority Medium (total 7)
buglabel.Board: #28, Status
In Progress(picked up immediately).docs: mark the JSON half of #2026 latent, not reproducible today
cliffhall commentedon Aug 16, 2026
Fixed by #2027, merged to
v2/main.The two draft-holding fields now receive the raw value and the schema default separately and compare only against the raw one, so a default seeds what the field opens with rather than being re-imposed on every unsendable draft. The number field could not be emptied before; the JSON editor's half was latent (same-reference seeding) and is closed alongside it.
Ships in v2.3.0.
fix(web): stop a schema default from reverting the edit in progress (m…