Fix notion-sync header crash on non-ASCII titles
S3 user-metadata values are sent as HTTP headers, which must be US-ASCII. Notion page titles routinely contain em dashes and other non-ASCII characters, causing ERR_INVALID_CHAR and a daily sync failure. Sanitize the notion-title metadata to printable ASCII. Also treat a Bedrock ConflictException from StartIngestionJob as benign (an ingestion job is already running and will pick up the freshly uploaded files) instead of failing the whole invocation.
This commit is contained in:
parent
de55c0eeca
commit
67266ba57b
1 changed files with 35 additions and 10 deletions
|
|
@ -44,6 +44,17 @@ function getPageTitle(page: Record<string, any>): string {
|
|||
return page.id as string;
|
||||
}
|
||||
|
||||
// S3 user-metadata values travel as HTTP headers, which must be US-ASCII with no
|
||||
// control characters. Notion titles routinely contain em dashes and other
|
||||
// non-ASCII characters (ERR_INVALID_CHAR otherwise). Strip to safe printable
|
||||
// ASCII and cap at the 256-char metadata limit.
|
||||
function sanitizeMetadataValue(value: string): string {
|
||||
return value
|
||||
.replace(/[^\x20-\x7E]/g, '') // drop non-printable / non-ASCII
|
||||
.trim()
|
||||
.slice(0, 256);
|
||||
}
|
||||
|
||||
async function clearOldFiles(bucket: string): Promise<void> {
|
||||
let continuationToken: string | undefined;
|
||||
const toDelete: { Key: string }[] = [];
|
||||
|
|
@ -128,7 +139,7 @@ export const handler = async (): Promise<void> => {
|
|||
ContentType: 'text/markdown',
|
||||
Metadata: {
|
||||
'notion-page-id': pageId,
|
||||
'notion-title': title.slice(0, 256), // S3 metadata value limit
|
||||
'notion-title': sanitizeMetadataValue(title),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -144,13 +155,27 @@ export const handler = async (): Promise<void> => {
|
|||
console.log(`Upload complete. ${synced} synced, ${failed} failed.`);
|
||||
|
||||
console.log('Triggering Bedrock KB ingestion job...');
|
||||
const ingestionRes = await bedrockAgent.send(
|
||||
new StartIngestionJobCommand({
|
||||
knowledgeBaseId: kbId,
|
||||
dataSourceId: dsId,
|
||||
}),
|
||||
);
|
||||
console.log(
|
||||
`Ingestion job started: ${ingestionRes.ingestionJob?.ingestionJobId}`,
|
||||
);
|
||||
try {
|
||||
const ingestionRes = await bedrockAgent.send(
|
||||
new StartIngestionJobCommand({
|
||||
knowledgeBaseId: kbId,
|
||||
dataSourceId: dsId,
|
||||
}),
|
||||
);
|
||||
console.log(
|
||||
`Ingestion job started: ${ingestionRes.ingestionJob?.ingestionJobId}`,
|
||||
);
|
||||
} catch (err) {
|
||||
// A ConflictException means an ingestion job is already running for this data
|
||||
// source (e.g. an overlapping run). The freshly uploaded files will be picked
|
||||
// up by that in-flight job, so this is benign — log and exit cleanly rather
|
||||
// than failing the whole invocation.
|
||||
if (err instanceof Error && err.name === 'ConflictException') {
|
||||
console.log(
|
||||
'Ingestion job already in progress; uploaded files will be picked up by the running job. Skipping.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Reference in a new issue