Skip to content

[P3 bug] null on optional:true numeric field is rejected, while omission is accepted — asymmetric absence #22

@Sam-Bolling

Description

@Sam-Bolling

Summary

For a numeric Quantity field with optional: true, omitting the field is accepted (HTTP 201) but sending it with null is rejected (HTTP 400 must be a number). Both forms are user intent for "no value here", and the asymmetry is undocumented and confusing.

Reproduction

Datastream with baro_altitude declared as Quantity with "optional": true.

T14: POST observations  body={"result":{"geo_altitude":10500}}
  → HTTP 201 ✓ (omitted is OK)

T15: POST observations  body={"result":{"baro_altitude":null,"geo_altitude":10500}}
  → HTTP 400 {"error":"... result.baro_altitude must be a number"}

Full transcript: docs/research/evidence/issue-004/nan-tests-head-2026-04-30.txt (cases T14, T15).

Source

In internal/api/observation_schema_validation.go, the datarecord branch only checks for field absence to short-circuit on optional: true. When the field is present with null, control reaches the quantity branch with value = nil, the float64 type assertion fails, and the error fires.

Proposed fix

In the datarecord (and vector / dataarray) branch, treat value == nil (a JSON null) as equivalent to absence when the field is optional: true:

fieldVal, present := record[field.Name]
if (!present || fieldVal == nil) {
    if field.Optional != nil && *field.Optional { continue }
    return fmt.Errorf("%s.%s is required by datastream schema", path, field.Name)
}

Priority

P3 — UX / consistency; not a security or data-loss bug, but it forces clients to special-case absent vs explicit-null in their result encoders. Same diagnostic pattern documented in #19 for phenomenonTime vs resultTime: two ways of expressing absence should converge.

Cross-refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions