Repository navigation
Upload completion returns 500 when an optional file group is omitted #1648
Description
POST /v1/instrument-records/:id/files/upload-complete returns a 500 when the payload leaves out an optional file group (one whose count.min is 0). validateFiles accepts the missing group in its count check (it counts it as 0 files), then iterates it through a non-null assertion and throws TypeError: uploadedFiles is not iterable. The payload is valid under $UploadCompleteData (uploads: z.record(z.string(), z.array($FileMetadata))), so the API should complete the upload. Instead the record stays pending even though the files are already in storage. The web client avoids this only because apps/web/src/routes/_app/instruments/render/$id.tsx (lines 74-76) pre-fills every basename with []. Any other API client that omits an empty optional group hits the crash.
Where
apps/api/src/instrument-records/files/files.service.ts:174-185
const uploadedFiles = uploads[fileGroup.basename];
const actual = uploadedFiles?.length ?? 0;
// count check passes when min === 0 and the key is absent
for (const file of uploadedFiles!) {
validatedFiles.push(file);
}Reproduce
- Create a pending record for a file instrument with a group whose
countis{ min: 0, max: 2 }(for example, basenamenotes). POST /v1/instrument-records/<id>/files/upload-completewith{ "uploads": {} }.
Actual: 500 (TypeError: uploadedFiles is not iterable), and the record stays pending.
Expected: 201, and the record is marked complete with no files for notes.
Tests
apps/api/src/instrument-records/files/__tests__/files.service.spec.ts, test should crash with a type error when an optional group is omitted, documenting a bug in validateFiles (line 322). It asserts .rejects.toThrow(TypeError). When fixing, rename it to describe the correct behaviour (for example, "should complete the upload when an optional group is omitted"). Then assert that instrumentRecordModel.update is called with files: { create: [] } and pending: false.
Suggested fix
Default the lookup to an empty array (const uploadedFiles = uploads[fileGroup.basename] ?? [];) and drop the !.
Found while raising unit test coverage in #1632.
Activity