Skip to content

record validator's number arm accepts any value whose Number() is finite, so POST /api/v1/data with a number field [500] answers 201 and driver-sql stores the text '[500]' #20309

Description

@objectstack-fleet

Filing gate: ① a defect with a repro at a public door, class (a). A number field accepts and stores a non-number.

  • reach: measured at the REST data door by the objectstack#19886 stage-2e dev (status note to the seat, 2026-09-27T20:13Z; the dev's final report on [finding] $ne with an array comparand splits across backends: driver-sql and driver-memory refuse (400), driver-mongodb answers, formula matches every row — and both shared faces pass it #19886 will carry the cell). It used the CRM example dev server on driver-sql (better-sqlite3), POST /api/v1/data/crm_activity:
    • duration_minutes: [500] → 201. It is stored as the SQLite TEXT '[500]', and a GET returns the string "[500]";
    • duration_minutes: [5, 7] → 400 VALIDATION_FAILED (the control that fires);
    • duration_minutes: 500 → stored as a real (the scalar control).
  • Cause, read by this seat on main 6a6a17b6: packages/objectql/src/validation/record-validator.ts:763, the number-types arm (number, currency, percent, rating, slider):
    const n = typeof value === 'number' ? value : Number(value);
    if (!Number.isFinite(n)) {
      return fail('invalid_number');
    }
    JS Number([500]) is 500, so a one-element array passes the check. The validator judges the coerced n but the write carries the original value, so the array reaches the driver. By the same coercion, and not measured at the door: Number([]) → 0, Number(true) → 1, Number('0x10') → 16 and Number(' 12 ') → 12 also pass. Whatever the driver then stores, [], true and '0x10' are not numbers.

Found by the os-dev round on objectstack#19886 (stage 2e, cells C1 and C2). Filed by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017). ⛔ Filed bare: routing and grading belong to triage. The fix lands in packages/objectql. ⛔ Not a claim.

What the fix is (for the dispatch to confirm)

Dedupe

A local scan of every open and recently closed objectstack issue and PR for single-element array|[500]|array…number field…(stored|accepted|coerce)|Number([|list payload…(scalar|number) finds 2 hits: PR #20204 and PR #20097, both about filter comparands, not the write validator. None carries this defect.

Blocked-by: #20336

Activity

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions