Skip to content

Commit 2f83dae

Browse files
Skip and log singleton corrupted project records
1 parent 8b4bac0 commit 2f83dae

2 files changed

Lines changed: 22 additions & 6 deletions

File tree

src/__tests__/services/projectStorage.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,12 +491,18 @@ describe('projectStorage', () => {
491491
await expect(listProjects(token)).rejects.toThrow(SyntaxError);
492492
});
493493

494-
it('propagates a JSON parse error from getProject as a corrupt-record signal', async () => {
494+
it('skips a project whose storage value is corrupt JSON and logs the error', async () => {
495495
__mockReadUserData
496496
.mockResolvedValueOnce(JSON.stringify(['abc']))
497497
.mockResolvedValueOnce('not valid json');
498498

499-
await expect(listProjects(token)).rejects.toThrow(SyntaxError);
499+
const result = await listProjects(token);
500+
501+
expect(result).toEqual([]);
502+
expect(__mockLogger.error).toHaveBeenCalledWith(
503+
expect.stringContaining('abc'),
504+
expect.any(SyntaxError),
505+
);
500506
});
501507
});
502508
});

src/services/projectStorage.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,26 @@ export async function getProject(
175175

176176
/**
177177
* Returns all stored projects in creation order. Projects whose storage keys are missing (e.g.
178-
* after a failed delete) are silently omitted.
178+
* after a failed delete) are silently omitted. Projects that fail to read or parse are logged and
179+
* skipped so a single corrupted record does not prevent access to the rest.
179180
*
180181
* @param token - The execution token for storage access.
181182
* @returns All stored projects, ordered by creation time.
182-
* @throws {SyntaxError} If `projectIds` or any project's storage value contains invalid JSON.
183-
* @throws If `papi.storage.readUserData` rejects for any non-ENOENT reason.
183+
* @throws {SyntaxError} If `projectIds` contains invalid JSON.
184+
* @throws If `papi.storage.readUserData` rejects for any non-ENOENT reason when reading the index.
184185
*/
185186
export async function listProjects(token: ExecutionToken): Promise<InterlinearProject[]> {
186187
const ids = await readIds(token);
187-
const projects = await Promise.all(ids.map((id) => getProject(token, id)));
188+
const projects = await Promise.all(
189+
ids.map(async (id) => {
190+
try {
191+
return await getProject(token, id);
192+
} catch (e) {
193+
logger.error(`Interlinearizer: failed to read project ${id}:`, e);
194+
return undefined;
195+
}
196+
}),
197+
);
188198
return projects.filter((p): p is InterlinearProject => p !== undefined);
189199
}
190200

0 commit comments

Comments
 (0)