Skip to content

ObjectQL.cascadeDeleteRelations fails OPEN: a failed dependents probe skips the restrict guard entirely, so a delete that should be refused succeeds silently #8895

Description

@os-project-manager

Found while measuring the read-seam census for #8845 (measurement card; no fix attempted there). Filed unassigned. Not a claim.

Measured on origin/main @ 8664a2c.

The seam

packages/objectql/src/engine.ts, in cascadeDeleteRelations() (catch at :9896):

letdependents: any[];try{dependents=awaitthis.find(childName,{where: {[fieldName]: id}, context }asany);}catch{continue;}if(!dependents||dependents.length===0)continue;if(behavior==='restrict'){// ... refuse the delete with DELETE_RESTRICTED}

The continue is reached whenever the dependents probe fails for any reason. Control then moves to the next child relation, and the effect is exactly as if the probe had returned zero rows:

  • behavior === 'restrict' never fires, so a delete that the integrity rules say must be refused is allowed through;
  • behavior === 'set_null' / 'cascade' never runs, so child rows that should have been nulled or removed are left orphaned;
  • nothing is logged and nothing is returned to the caller, so the delete reports success.

This is fail-OPEN on a referential-integrity guard. The comparable seams in the same family fail closed or report.

Why it is not caught today

check:durability-log-level's read-seam invention rule classifies a catch by the expression it returns. This one returns nothing — it jumps — so there is no expression to classify and the seam is counted in the census and cleared as no invented answer. That blind spot is #8845's subject; #8845 measured it and deliberately did not extend the rule (the reasons are recorded in the checker's header). So this seam needs fixing on its own terms, not by waiting for a gate.

Suggested shape

The repo already has the vocabulary for this: discriminate the error's type and only treat the benign case as "no dependents".

}catch(error){if(isMissingTableError(error))continue;throwerror;}

A child table that has not been provisioned genuinely has no dependents, so continue is truthful there. Any other failure means the guard could not be evaluated, and a referential-integrity guard that could not be evaluated must not silently pass.

Notes

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions