Skip to content

Commit 35949bb

Browse files
committed
fix(opencode): harden root path and retry errors
1 parent 35fac8d commit 35949bb

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

apps/sim/lib/opencode/service.test.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @vitest-environment node
33
*/
44

5-
import { describe, expect, it, vi } from 'vitest'
5+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
66

77
const { mockCreateOpenCodeClient } = vi.hoisted(() => ({
88
mockCreateOpenCodeClient: vi.fn(),
@@ -39,10 +39,19 @@ vi.mock('@/lib/opencode/client', () => ({
3939
}))
4040

4141
import {
42+
listOpenCodeRepositories,
4243
promptOpenCodeSession,
4344
shouldRetryWithFreshOpenCodeSession,
4445
} from '@/lib/opencode/service'
4546

47+
beforeEach(() => {
48+
vi.clearAllMocks()
49+
})
50+
51+
afterEach(() => {
52+
vi.unstubAllEnvs()
53+
})
54+
4655
describe('shouldRetryWithFreshOpenCodeSession', () => {
4756
it('returns true for stale-session errors', () => {
4857
expect(shouldRetryWithFreshOpenCodeSession(new Error('404 session not found'))).toBe(true)
@@ -56,6 +65,36 @@ describe('shouldRetryWithFreshOpenCodeSession', () => {
5665
expect(shouldRetryWithFreshOpenCodeSession('model not found')).toBe(false)
5766
expect(shouldRetryWithFreshOpenCodeSession('provider does not exist')).toBe(false)
5867
})
68+
69+
it('does not crash for undefined, symbol, or function errors', () => {
70+
expect(() => shouldRetryWithFreshOpenCodeSession(undefined)).not.toThrow()
71+
expect(() => shouldRetryWithFreshOpenCodeSession(Symbol('session'))).not.toThrow()
72+
expect(() => shouldRetryWithFreshOpenCodeSession(() => 'session')).not.toThrow()
73+
expect(shouldRetryWithFreshOpenCodeSession(undefined)).toBe(false)
74+
})
75+
})
76+
77+
describe('listOpenCodeRepositories', () => {
78+
it('handles OPENCODE_REPOSITORY_ROOT set to / without double slashes', async () => {
79+
vi.stubEnv('OPENCODE_REPOSITORY_ROOT', '/')
80+
81+
mockCreateOpenCodeClient.mockReturnValue({
82+
project: {
83+
list: vi.fn().mockResolvedValue({
84+
data: [{ id: 'project-1', worktree: '/repo-a' }],
85+
}),
86+
},
87+
})
88+
89+
await expect(listOpenCodeRepositories()).resolves.toEqual([
90+
{
91+
id: 'repo-a',
92+
label: 'repo-a',
93+
directory: '/repo-a',
94+
projectId: 'project-1',
95+
},
96+
])
97+
})
5998
})
6099

61100
describe('promptOpenCodeSession', () => {

apps/sim/lib/opencode/service.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function getOpenCodeRepositoryRoot(): string {
8585
}
8686

8787
if (configuredRoot === '/') {
88-
return configuredRoot
88+
return ''
8989
}
9090

9191
return configuredRoot.replace(/\/+$/, '')
@@ -516,12 +516,7 @@ export function buildOpenCodeSessionTitle(repository: string, userKey: string):
516516
}
517517

518518
export function shouldRetryWithFreshOpenCodeSession(error: unknown): boolean {
519-
const message =
520-
error instanceof Error
521-
? error.message
522-
: typeof error === 'string'
523-
? error
524-
: JSON.stringify(error)
519+
const message = getOpenCodeRetryErrorMessage(error)
525520

526521
const normalized = message.toLowerCase()
527522
const staleSessionPatterns = [
@@ -540,6 +535,25 @@ export function shouldRetryWithFreshOpenCodeSession(error: unknown): boolean {
540535
return normalized.includes('404') && normalized.includes('session')
541536
}
542537

538+
function getOpenCodeRetryErrorMessage(error: unknown): string {
539+
if (error instanceof Error) {
540+
return error.message
541+
}
542+
543+
if (typeof error === 'string') {
544+
return error
545+
}
546+
547+
try {
548+
const serialized = JSON.stringify(error)
549+
if (typeof serialized === 'string') {
550+
return serialized
551+
}
552+
} catch {}
553+
554+
return String(error ?? '')
555+
}
556+
543557
export async function logOpenCodeFailure(
544558
message: string,
545559
error: unknown

0 commit comments

Comments
 (0)