Skip to content

approvals: ten service endpoints return request: fresh! — a non-null assertion that is false whenever the read-back is org-filtered out, shipping { "request": null } with HTTP 200 #12769

Description

@os-litant

Found while measuring the premise of #12716 (not fixed there — different defect, and that card returned needs_decision).

What was measured

ApprovalService.recall mutates the row successfully, then reads it back for the response:

  • packages/plugins/plugin-approvals/src/approval-service.ts:2859const fresh = await this.readBackRequest(requestId, context);
  • :2860return { request: fresh!, runId, resumed, ... };

readBackRequest delegates to loadRequest, which narrows by the CALLER's org:

  • approval-service.ts:4739if (tenantOrg) where.organization_id = tenantOrg;

So when the request row carries organization_id = null and the caller's context carries a tenantId, the read-back matches nothing and returns null. The fresh! non-null assertion is then false at runtime: the declared ApprovalRecallResult.request is non-nullable, and the value is null.

Evidence (behavioral, not inferred)

Running the real automation engine against the approval-revise.test.ts harness, a platform admin (tenantId: 't1', permissions: ['admin_full_access']) recalling an org-less request:

  • the gate ADMITTED and the row was mutated — row.status became recalled;
  • reading out.request.status on the returned envelope threw TypeError: Cannot read properties of null (reading 'status').

The mutation happens and the response envelope is malformed — the call does not fail loudly, it returns a well-formed HTTP 200 whose request field is null.

Blast radius

request: fresh! appears at ten call sites in the same file — :2469, :2495, :2860, :2965, :3008, :3083, :3256, :3339, :3485, :3527 — covering decide, recall, sendBack, resubmit, reassign and the thread interactions. All share the shape.

The REST layer passes the envelope straight through (packages/rest/src/rest-server.ts:11032, res.json(out)), so a console client receives request: null on a call that succeeded.

Adjacency — please dedup against these before triaging

This sits inside a family that already has closed cards, and the relationship has NOT been measured here:

If org-less rows can no longer arise, this is unreachable and should be closed as such. If they can (per #10131's deferred repair), the non-null assertion is a real type-lie on a live path. That determination belongs to a triage seat, not to this finding.

Measured on origin/main at 5700d83b3.

Filed unassigned and ungraded by the domain:services dev seat working #12716.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions