fix: message handle (#14178)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Robustly sanitize session titles, messages, attachments, and embedded
data to remove invalid/null characters and prevent corrupt persistence.
* Improve chat title generation to skip or recover from invalid input
and log contextual errors without crashing.
* Add more detailed storage and workspace logs and reduce repetitive
checks to aid troubleshooting and stability.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2025-12-29 21:47:53 +08:00
committed by GitHub
parent 1bfd29df99
commit e12fe9c12b
4 changed files with 170 additions and 17 deletions
@@ -533,7 +533,15 @@ export class CopilotEmbeddingJob {
workspaceId
);
if (!snapshot) {
this.logger.warn(`workspace snapshot ${workspaceId} not found`);
// maybe local workspace or empty workspace
this.logger.verbose(`workspace root snapshot ${workspaceId} not found`);
// mark last check time to avoid repeated checking
await this.models.workspace.update(
workspaceId,
{ lastCheckEmbeddings: new Date() },
false
);
return;
} else if (
// always check if never cleared
@@ -320,6 +320,20 @@ export class ChatSessionService {
return messages.data;
}
private stripNullBytes(value?: string | null): string {
if (!value) return '';
return value.replace(/\u0000/g, '');
}
private isNullByteError(error: unknown): boolean {
return (
error instanceof Error &&
(error.message.includes('\\u0000') ||
error.message.includes('unsupported Unicode escape sequence') ||
error.message.includes('22P05'))
);
}
private async getHistory(session: Session): Promise<SessionHistory> {
const prompt = await this.prompt.get(session.promptName);
if (!prompt) throw new CopilotPromptNotFound({ name: session.promptName });
@@ -655,7 +669,13 @@ export class ChatSessionService {
);
return;
}
const { userId, title, messages } = session;
const { userId, title } = session;
const messages =
session.messages?.map(m => ({
...m,
content: this.stripNullBytes(m.content),
})) ?? [];
if (
title ||
!messages.length ||
@@ -665,18 +685,41 @@ export class ChatSessionService {
return;
}
{
const title = await this.chatWithPrompt('Summary as title', {
content: session.messages
.map(m => `[${m.role}]: ${m.content}`)
.join('\n'),
});
await this.models.copilotSession.update({ userId, sessionId, title });
const promptContent = messages
.map(m => `[${m.role}]: ${m.content}`)
.join('\n');
const generatedTitle = this.stripNullBytes(
await this.chatWithPrompt('Summary as title', {
content: promptContent,
})
).trim();
if (!generatedTitle) {
this.logger.warn(
`Generated empty title for session ${sessionId}, skip updating`
);
return;
}
await this.models.copilotSession.update({
userId,
sessionId,
title: generatedTitle,
});
} catch (error) {
console.error(
const context = {
sessionId,
cause: error instanceof Error ? error.cause : error,
};
if (this.isNullByteError(error)) {
this.logger.warn(
`Skip title generation for session ${sessionId} due to invalid null bytes in stored data`,
context
);
return;
}
this.logger.error(
`Failed to generate title for session ${sessionId}:`,
error
context
);
throw error;
}