Skip to content

Commit 2cbbf09

Browse files
committed
Add SESSION_ALREADY_EXISTS error code for session conflicts
Replaces RESOURCE_BUSY with a semantically correct error code that properly indicates a uniqueness constraint violation rather than a temporary lock.
1 parent 2822051 commit 2cbbf09

File tree

11 files changed

+47
-6
lines changed

11 files changed

+47
-6
lines changed

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ Turbo handles task orchestration (`turbo.json`) with dependency-aware builds.
293293

294294
**Focus on the change, not how it was discovered** - never reference "review feedback", "PR comments", or "code review" in commit messages. Describe what the change does and why, not that someone asked for it.
295295

296+
**Avoid bullet points** - write prose, not lists. If you need bullets to explain a change, you're either committing too much at once or over-explaining implementation details. The body should be a brief paragraph, not a changelog.
297+
296298
Good examples:
297299

298300
```

packages/sandbox-container/src/services/session-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class SessionManager {
3232
success: false,
3333
error: {
3434
message: `Session '${options.id}' already exists`,
35-
code: ErrorCode.RESOURCE_BUSY,
35+
code: ErrorCode.SESSION_ALREADY_EXISTS,
3636
details: {
3737
sessionId: options.id
3838
}

packages/sandbox/src/errors/adapter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type {
2626
PortNotExposedContext,
2727
ProcessErrorContext,
2828
ProcessNotFoundContext,
29+
SessionAlreadyExistsContext,
2930
ValidationFailedContext
3031
} from '@repo/shared/errors';
3132
import { ErrorCode } from '@repo/shared/errors';
@@ -58,6 +59,7 @@ import {
5859
ProcessNotFoundError,
5960
SandboxError,
6061
ServiceNotRespondingError,
62+
SessionAlreadyExistsError,
6163
ValidationFailedError
6264
} from './classes';
6365

@@ -123,6 +125,12 @@ export function createErrorFromResponse(errorResponse: ErrorResponse): Error {
123125
errorResponse as unknown as ErrorResponse<ProcessErrorContext>
124126
);
125127

128+
// Session Errors
129+
case ErrorCode.SESSION_ALREADY_EXISTS:
130+
return new SessionAlreadyExistsError(
131+
errorResponse as unknown as ErrorResponse<SessionAlreadyExistsContext>
132+
);
133+
126134
// Port Errors
127135
case ErrorCode.PORT_ALREADY_EXPOSED:
128136
return new PortAlreadyExposedError(

packages/sandbox/src/errors/classes.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
ProcessExitedBeforeReadyContext,
2929
ProcessNotFoundContext,
3030
ProcessReadyTimeoutContext,
31+
SessionAlreadyExistsContext,
3132
ValidationFailedContext
3233
} from '@repo/shared/errors';
3334

@@ -236,6 +237,25 @@ export class ProcessError extends SandboxError<ProcessErrorContext> {
236237
}
237238
}
238239

240+
// ============================================================================
241+
// Session Errors
242+
// ============================================================================
243+
244+
/**
245+
* Error thrown when a session already exists
246+
*/
247+
export class SessionAlreadyExistsError extends SandboxError<SessionAlreadyExistsContext> {
248+
constructor(errorResponse: ErrorResponse<SessionAlreadyExistsContext>) {
249+
super(errorResponse);
250+
this.name = 'SessionAlreadyExistsError';
251+
}
252+
253+
// Type-safe accessors
254+
get sessionId() {
255+
return this.context.sessionId;
256+
}
257+
}
258+
239259
// ============================================================================
240260
// Port Errors
241261
// ============================================================================

packages/sandbox/src/errors/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export {
109109
ProcessReadyTimeoutError,
110110
SandboxError,
111111
ServiceNotRespondingError,
112+
// Session Errors
113+
SessionAlreadyExistsError,
112114
// Validation Errors
113115
ValidationFailedError
114116
} from './classes';

packages/sandbox/src/sandbox.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
ErrorCode,
3939
ProcessExitedBeforeReadyError,
4040
ProcessReadyTimeoutError,
41-
SandboxError
41+
SessionAlreadyExistsError
4242
} from './errors';
4343
import { CodeInterpreter } from './interpreter';
4444
import { isLocalhostPattern } from './request-handler';
@@ -1015,10 +1015,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
10151015
this.logger.debug('Default session initialized', { sessionId });
10161016
} catch (error: unknown) {
10171017
// Session may already exist (e.g., after hot reload or concurrent request)
1018-
if (
1019-
error instanceof SandboxError &&
1020-
error.code === ErrorCode.RESOURCE_BUSY
1021-
) {
1018+
if (error instanceof SessionAlreadyExistsError) {
10221019
this.logger.debug(
10231020
'Session exists in container but not in DO state, syncing',
10241021
{ sessionId }

packages/shared/src/errors/codes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export const ErrorCode = {
4343
PROCESS_PERMISSION_DENIED: 'PROCESS_PERMISSION_DENIED',
4444
PROCESS_ERROR: 'PROCESS_ERROR',
4545

46+
// Session Errors (409)
47+
SESSION_ALREADY_EXISTS: 'SESSION_ALREADY_EXISTS',
48+
4649
// Port Errors (409)
4750
PORT_ALREADY_EXPOSED: 'PORT_ALREADY_EXPOSED',
4851
PORT_IN_USE: 'PORT_IN_USE',

packages/shared/src/errors/contexts.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export interface ProcessErrorContext {
4848
stderr?: string;
4949
}
5050

51+
export interface SessionAlreadyExistsContext {
52+
sessionId: string;
53+
}
54+
5155
/**
5256
* Process readiness error contexts
5357
*/

packages/shared/src/errors/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export type {
5757
ProcessExitedBeforeReadyContext,
5858
ProcessNotFoundContext,
5959
ProcessReadyTimeoutContext,
60+
SessionAlreadyExistsContext,
6061
ValidationFailedContext
6162
} from './contexts';
6263
// Export utility functions

packages/shared/src/errors/status-map.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const ERROR_STATUS_MAP: Record<ErrorCode, number> = {
4242
[ErrorCode.PORT_ALREADY_EXPOSED]: 409,
4343
[ErrorCode.PORT_IN_USE]: 409,
4444
[ErrorCode.RESOURCE_BUSY]: 409,
45+
[ErrorCode.SESSION_ALREADY_EXISTS]: 409,
4546

4647
// 502 Bad Gateway
4748
[ErrorCode.SERVICE_NOT_RESPONDING]: 502,

0 commit comments

Comments
 (0)