Skip to content

seed-loader resolveSoleOrganizationId swallows every sys_organization read failure behind a comment naming one benign cause, so a transient outage silently seeds org-less rows #12852

Description

@claude

Found while triaging the parameterless-catch read seams for #12753 (see PR #12851 for the full 11-site triage). Filed unassigned, and ⛔ with no domain:* — that is triage's single-producer field, per the convention recorded on #8901.

⛔ Not repaired in #12851: that PR's scope is a gate-header record, and this is a source fix in another package.

The seam

packages/metadata-protocol/src/seed-loader.ts, resolveSoleOrganizationId():

try {
  const rows = await this.engine.find('sys_organization', { fields: ['id'], limit: 2, context: { isSystem: true } } as any);
  if (Array.isArray(rows) && rows.length === 1) { ... return id ? String(id) : undefined; }
} catch {
  // sys_organization may not exist (single-tenant runtime) - ignore.
}
return undefined;

Why it is wrong

The comment names ONE benign cause and the catch swallows every cause. This is the #4728 shape verbatim: a real outage, a permission refusal or a driver fault is answered exactly like "this runtime has no sys_organization table".

The method's own JSDoc states the consequence: the value is used "to stamp business seed rows when the caller pinned no config.organizationId", and without it the seeds land "org-less (→ invisible under strict org-scoping)". So a transient read failure during seeding writes rows that are silently invisible afterwards, and nothing in the run says so — the seed report has an errors field, and this path does not touch it.

Note the asymmetry that makes this cheap to justify: the sibling probe on the objectql side, probeInstallOrganizations, had the SAME shape and was repaired by PR #9817 to bind the parameter and ask the declared predicate. This one was not.

The fix, which is the declared one

} catch (error) {
  if (!isMissingTableError(error)) throw error;   // packages/metadata/src/errors.ts
}

isMissingTableError is the single entry in READ_FAILURE_DISCRIMINATORS in scripts/check-durability-degradation-log-level.mjs and is exactly the benign class the comment already names, so the discrimination costs nothing and the JSDoc's "or when sys_organization is absent" stays true — while every other cause stops being answered as an emptiness.

Measurement context

This seam is 1 of 11 read seams with a parameterless catch on origin/main @ 6f0fec3d0, and 1 of only 2 of those 11 graded genuinely wrong rather than correct-but-undeclared. It is not covered by #8901's cohort (no accumulator above the try), and not by #8896 / #8906 / #8895, all closed and none naming it.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions