diff --git a/packages/opencode/src/cli/cmd/stats.ts b/packages/opencode/src/cli/cmd/stats.ts index d78c4f0abd..9d0141c432 100644 --- a/packages/opencode/src/cli/cmd/stats.ts +++ b/packages/opencode/src/cli/cmd/stats.ts @@ -86,13 +86,13 @@ async function getAllSessions(): Promise { const sessions: Session.Info[] = [] const projectKeys = await Storage.list(["project"]) - const projects = await Promise.all(projectKeys.map((key) => Storage.read(key))) + const projects = await Promise.all(projectKeys.map((key) => Storage.read(key).catch(() => undefined))) for (const project of projects) { if (!project) continue const sessionKeys = await Storage.list(["session", project.id]) - const projectSessions = await Promise.all(sessionKeys.map((key) => Storage.read(key))) + const projectSessions = await Promise.all(sessionKeys.map((key) => Storage.read(key).catch(() => undefined))) for (const session of projectSessions) { if (session) { diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts index 611b692897..4de6546230 100644 --- a/packages/opencode/src/storage/storage.ts +++ b/packages/opencode/src/storage/storage.ts @@ -174,8 +174,17 @@ export namespace Storage { if (!content.trim()) { throw new NotFoundError({ message: `Empty file: ${target}` }) } - const result = JSON.parse(content) - return result as T + const hasControlCharacters = /[\x00-\x08\x0B\x0C\x0E-\x1F]/.test(content) + if (hasControlCharacters) { + throw new NotFoundError({ message: `Corrupted file detected: ${target}` }) + } + try { + const result = JSON.parse(content) + return result as T + } catch (e) { + const message = e instanceof Error ? e.message : String(e) + throw new NotFoundError({ message: `Failed to parse JSON from ${target}: ${message}` }) + } }) } @@ -188,10 +197,19 @@ export namespace Storage { if (!content.trim()) { throw new NotFoundError({ message: `Empty file: ${target}` }) } - const parsed = JSON.parse(content) - fn(parsed) - await Bun.write(target, JSON.stringify(parsed, null, 2)) - return parsed as T + const hasControlCharacters = /[\x00-\x08\x0B\x0C\x0E-\x1F]/.test(content) + if (hasControlCharacters) { + throw new NotFoundError({ message: `Corrupted file detected: ${target}` }) + } + try { + const parsed = JSON.parse(content) + fn(parsed) + await Bun.write(target, JSON.stringify(parsed, null, 2)) + return parsed as T + } catch (e) { + const message = e instanceof Error ? e.message : String(e) + throw new NotFoundError({ message: `Failed to parse JSON from ${target}: ${message}` }) + } }) }