Skip to content

Commit 37ea2ab

Browse files
author
Bob Strahan
committed
Fix UI error handling for orphaned DynamoDB entries
1 parent 280e997 commit 37ea2ab

File tree

2 files changed

+44
-18
lines changed

2 files changed

+44
-18
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ SPDX-License-Identifier: MIT-0
55

66
## [Unreleased]
77

8+
### Fixed
9+
10+
- **UI Robustness for Orphaned List Entries** - [#102](https://github.com/aws-solutions-library-samples/accelerated-intelligent-document-processing-on-aws/issues/102)
11+
- Fixed UI error banner "failed to get document details - please try again later" appearing when orphaned list entries exist (list# items without corresponding doc# items in DynamoDB tracking table)
12+
- **Root Cause**: When a document had a list entry but no corresponding document record, the error would trigger UI banner and prevent display of all documents in the same time shard
13+
- **Solution**: Enhanced error handling to gracefully handle missing documents - now only shows error banner if ALL documents fail to load, not just one
14+
- **Enhanced Debugging**: Added detailed console logging with full PK/SK information for both list entries and expected document entries to facilitate cleanup of orphaned records
15+
- **User Impact**: All valid documents now display correctly even when orphaned list entries exist; debugging information available in browser console for identifying problematic entries
16+
817
## [0.3.21]
918

1019
### Added

src/ui/src/hooks/use-graphql-api.js

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,21 @@ const useGraphQlApi = ({ initialPeriodsToLoad = DOCUMENT_LIST_SHARDS_PER_DAY * 2
8181
.filter((item) => !item.doc)
8282
.map((item) => item.key);
8383

84-
// Only show error banner if ALL documents failed
85-
if (getDocumentRejected.length === objectKeys.length) {
86-
setErrorMessage('failed to get document details - please try again later');
87-
logger.error('All document queries rejected', getDocumentRejected);
88-
} else if (getDocumentRejected.length > 0 || getDocumentNull.length > 0) {
89-
// Partial failure - log to console but don't block UI
90-
if (getDocumentRejected.length > 0) {
91-
logger.warn(`Failed to load ${getDocumentRejected.length} document(s) due to query rejection`);
92-
logger.debug('Rejected promises:', getDocumentRejected);
93-
}
94-
if (getDocumentNull.length > 0) {
95-
logger.warn(`${getDocumentNull.length} document(s) not found (returned null):`, getDocumentNull);
96-
logger.debug(
97-
'These documents have list entries but no corresponding document records - possible orphaned list entries',
98-
);
99-
}
84+
// Log partial failures but NEVER show error banner for individual document failures
85+
if (getDocumentRejected.length > 0) {
86+
logger.warn(
87+
`Failed to load ${getDocumentRejected.length} of ${objectKeys.length} document(s) due to query rejection`,
88+
);
89+
logger.debug('Rejected promises:', getDocumentRejected);
90+
}
91+
if (getDocumentNull.length > 0) {
92+
logger.warn(
93+
`${getDocumentNull.length} of ${objectKeys.length} document(s) not found (returned null):`,
94+
getDocumentNull,
95+
);
96+
logger.warn(
97+
'These documents have list entries but no corresponding document records - possible orphaned list entries',
98+
);
10099
}
101100

102101
// Filter out null/undefined documents to prevent downstream errors
@@ -281,6 +280,19 @@ const useGraphQlApi = ({ initialPeriodsToLoad = DOCUMENT_LIST_SHARDS_PER_DAY * 2
281280
const documentData = await documentDataPromise;
282281
const objectKeys = documentData.map((item) => item.ObjectKey);
283282
const documentDetails = await getDocumentDetailsFromIds(objectKeys);
283+
284+
// Log orphaned list entries with full PK/SK details for debugging
285+
const retrievedKeys = new Set(documentDetails.map((d) => d.ObjectKey));
286+
const missingDocs = documentData.filter((item) => !retrievedKeys.has(item.ObjectKey));
287+
if (missingDocs.length > 0) {
288+
missingDocs.forEach((item) => {
289+
logger.warn(`Orphaned list entry detected:`);
290+
logger.warn(` - List entry: PK="${item.PK}", SK="${item.SK}"`);
291+
logger.warn(` - Expected doc entry: PK="doc#${item.ObjectKey}", SK="none"`);
292+
logger.warn(` - ObjectKey: "${item.ObjectKey}"`);
293+
});
294+
}
295+
284296
// Merge document details with PK and SK, filtering out nulls to prevent shard-level failures
285297
return documentDetails
286298
.filter((detail) => detail != null)
@@ -306,9 +318,14 @@ const useGraphQlApi = ({ initialPeriodsToLoad = DOCUMENT_LIST_SHARDS_PER_DAY * 2
306318
setDocumentsDeduped(documentValuesReduced);
307319
setIsDocumentsListLoading(false);
308320
const getDocumentsRejected = getDocumentsPromiseResolutions.filter((r) => r.status === 'rejected');
309-
if (getDocumentsRejected.length) {
321+
// Only show error banner if ALL shard queries failed
322+
if (getDocumentsRejected.length === documentDataPromises.length) {
310323
setErrorMessage('failed to get document details - please try again later');
311-
logger.error('get document promises rejected', getDocumentsRejected);
324+
logger.error('All shard queries rejected', getDocumentsRejected);
325+
} else if (getDocumentsRejected.length > 0) {
326+
// Partial failure - log but don't show error banner
327+
logger.warn(`${getDocumentsRejected.length} of ${documentDataPromises.length} shard queries failed`);
328+
logger.debug('Rejected shard queries:', getDocumentsRejected);
312329
}
313330
} catch (error) {
314331
setIsDocumentsListLoading(false);

0 commit comments

Comments
 (0)