An add_data request for the developer-agreement dataset fails when saving the response. The transform completes, but save_response_to_db → _save_response_details → session.commit() raises:
sqlalchemy.exc.DataError: (psycopg2.errors.InvalidTextRepresentation)
invalid input syntax for type json
DETAIL: Token "NaN" is invalid.
CONTEXT: JSON data, line 1: ...national-park-authority:Q72617160", "entity": NaN...
[SQL: INSERT INTO response_details (response_id, detail) ...]
A Python float('nan') reaches a JSONB column. json.dumps emits a bare NaN (it allows non-finite floats by default); Postgres JSONB follows strict JSON and rejects it, so the whole batch insert fails.
Reproduction
- Request:
type: add_data, collection: developer-contributions, dataset: developer-agreement, New Forest NPA source (Developer-Agreement-2023.xlsx), resolved organisations = ['national-park-authority:Q72617158'].
- Fails with digital-land-python
main (0.1.1.dev210+g9240853a7).
- Passes with digital-land-python branch
109-bug-refresh-async-files-if-spec-has-updated.
Confirmed during debugging
- It takes the single-organisation path:
organisations has length 1, so EntityLookupPhase uses super().process(), not the new multi-org fan-out.
- Entity lookup works — rows resolve normally (e.g. reference
09/93881-da → entity 4601954).
- The issue log is clean — after
IssueLog.add_severity_column, self.rows contains no NaN (checked with [r for r in self.rows if any(v!=v for v in r.values())] → []). So pipeline-issues is not the source, despite the error text mentioning entity.
- Therefore the NaN is elsewhere in the response detail (
converted_row / transformed_row / a pipeline-summary breakdown). Exact location not yet pinned
Leading hypothesis (not yet proven)
A change on digital-land-python main that is absent from branch 109-... introduced this. Strongest suspects, both touching entity/organisation resolution.
Suspected interaction: a row that has an organisation value in a column plus a resource default organisation ends up producing a non-finite/blank entity that is serialised as a float NaN rather than "" / null.
Two separate problems
- Robustness (the crash): an unresolved/blank entity must never serialise as
NaN. Coerce non-finite floats → None before the JSONB write — either in digital-land-python where the value is produced, or defensively at the async-backend boundary in _save_response_details. This turns a hard FAILED into a gracefully-reported row.
- Behavioural (the trigger): identify the exact
main change producing the NaN for the org-column-plus-default case, and fix the resolution so the value is valid.
An
add_datarequest for thedeveloper-agreementdataset fails when saving the response. The transform completes, butsave_response_to_db→_save_response_details→session.commit()raises:A Python
float('nan')reaches aJSONBcolumn.json.dumpsemits a bareNaN(it allows non-finite floats by default); PostgresJSONBfollows strict JSON and rejects it, so the whole batch insert fails.Reproduction
type: add_data,collection: developer-contributions,dataset: developer-agreement, New Forest NPA source (Developer-Agreement-2023.xlsx), resolvedorganisations = ['national-park-authority:Q72617158'].main(0.1.1.dev210+g9240853a7).109-bug-refresh-async-files-if-spec-has-updated.Confirmed during debugging
organisationshas length 1, soEntityLookupPhaseusessuper().process(), not the new multi-org fan-out.09/93881-da→ entity4601954).IssueLog.add_severity_column,self.rowscontains no NaN (checked with[r for r in self.rows if any(v!=v for v in r.values())]→[]). Sopipeline-issuesis not the source, despite the error text mentioningentity.converted_row/transformed_row/ a pipeline-summary breakdown). Exact location not yet pinnedLeading hypothesis (not yet proven)
A change on digital-land-python
mainthat is absent from branch109-...introduced this. Strongest suspects, both touching entity/organisation resolution.Suspected interaction: a row that has an
organisationvalue in a column plus a resource default organisation ends up producing a non-finite/blankentitythat is serialised as a floatNaNrather than""/null.Two separate problems
NaN. Coerce non-finite floats →Nonebefore theJSONBwrite — either in digital-land-python where the value is produced, or defensively at the async-backend boundary in_save_response_details. This turns a hardFAILEDinto a gracefully-reported row.mainchange producing theNaNfor the org-column-plus-default case, and fix the resolution so the value is valid.