diff --git a/src/main/integrations.test.ts b/src/main/integrations.test.ts index 3decd711..3726cc1e 100644 --- a/src/main/integrations.test.ts +++ b/src/main/integrations.test.ts @@ -953,6 +953,25 @@ describe('IntegrationsManager', () => { expect(mock.integrationMountManager.ensureMounted).not.toHaveBeenCalled() }) + it('returns a missing preview when an in-scope remote read 404s instead of rejecting', async () => { + // Historical provider records (e.g. the GitHub issue JSON synthesized from + // Linear sync metadata) are not downloaded locally, so an in-scope read can + // legitimately 404. It must degrade to a missing preview rather than reject + // the IPC handler. + mock.relayClient.readFile.mockImplementationOnce(async (_workspaceId: string, path: string) => { + mock.readFileCalls.push({ workspaceId: _workspaceId, path }) + throw Object.assign(new Error('not found'), { status: 404, code: 'not_found' }) + }) + + const manager = new IntegrationsManager() + const preview = await manager.readRemoteFile( + 'project-1', + '/slack/channels/C123/messages/1713220123_001100.json' + ) + + expect(preview).toMatchObject({ kind: 'missing', size: 0 }) + }) + it('rejects targeted remote file reads outside the project integration scope', async () => { const manager = new IntegrationsManager() diff --git a/src/main/integrations.ts b/src/main/integrations.ts index 2d950257..7acc03a6 100644 --- a/src/main/integrations.ts +++ b/src/main/integrations.ts @@ -1060,10 +1060,27 @@ export class IntegrationsManager { throw new Error('Integration remote file is outside this project integration scope') } - return this.withIntegrationRemoteHandle(async (handle) => { - const file = await handle.client().readFile(handle.workspaceId, path) - return remoteFileReadToPreview(file) - }) + try { + return await this.withIntegrationRemoteHandle(async (handle) => { + const file = await handle.client().readFile(handle.workspaceId, path) + return remoteFileReadToPreview(file) + }) + } catch (error) { + // A remote 404 means the file was never synced locally (e.g. historical + // provider records that are not downloaded) or was removed between a + // directory listing and this read. Mirror the local filesystem behavior + // (readTextPreview) by reporting it as a missing preview instead of + // rejecting the IPC handler, so best-effort enrichment readers degrade + // gracefully rather than logging a handler error. + if (isHttpStatus(error, 404)) { + return { + kind: 'missing', + content: error instanceof Error ? error.message : 'Remote file not found', + size: 0 + } + } + throw error + } } async listRemoteDirectory(projectId: string, remotePath: string): Promise {