Skip to content

SeedLoader: per-record failure diagnostic embeds the composite externalId key, so its U+0000 joiner lands as a raw NUL in the server log #16488

Description

@os-sam

Observed

Booting an app whose seed declares a composite externalId (here ['employer', 'user'] on ats_employer_member), every per-record failure line the loader prints carries a raw NUL byte:

ERROR [SeedLoader] Failed to write ats_employer_member record #0 (employer+user=ats_employer-1788753956811-1<U+0000>usr_ats_quillstone_admin): the data engine rejected the write; ...

(<U+0000> marks where od -c shows \0 in the captured log.) One such line is enough for grep to classify the whole log as binary (Binary file matches), so every later grep -c / grep -n over that log silently returns nothing until the reader remembers -a. Measured on @objectstack/metadata-protocol 17.3.0 while investigating objectstack-ai/ats#20.

Cause

SeedLoaderService.externalIdKey() deliberately joins composite key parts with "\0" — its comment says so: a separator that cannot occur in a natural-key value, so ('a','b') and ('a\0b','') never collide. That is correct for the KEY. The defect is that the KEY string is what gets interpolated into the human-readable failure message (the (label=value) parenthetical), rather than a display rendering with a visible separator.

  • key: packages/metadata-protocol seed loader, externalIdKey(record, externalId)parts.join("\0")
  • label: the same class's externalIdLabel(externalId) already renders the FIELD names with + (employer+user); the VALUE side has no such rendering and reuses the key.

Expected

The diagnostic renders the composite value with a visible joiner (e.g. employer+user=ats_employer-…-1 + usr_ats_quillstone_admin, or JSON of the parts), keeping the NUL-joined string for map keys only. No control byte should reach a log line — this repo's own check-nul-bytes doctrine (a raw NUL flips a file to binary for grep) applies to emitted diagnostics as much as to source files.

Repro

Any seed dataset with externalId: ['a', 'b'] whose rows fail to write (a throwing hook is the easiest way); read the server log with grep -n 'Failed to write' — no -a — and observe Binary file matches.

Found while measuring objectstack-ai/ats#20; out of scope there, filed here where the fix lands. No assignee.

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions