Skip to content

Commit 9790a2e

Browse files
fix(kiloclaw): attach status 404 to 'Instance not provisioned' errors (#1388)
2 parents 29baf66 + f6040d7 commit 9790a2e

2 files changed

Lines changed: 48 additions & 4 deletions

File tree

kiloclaw/src/durable-objects/kiloclaw-instance.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,20 @@ afterEach(() => {
322322
});
323323

324324
describe('two-phase destroy', () => {
325+
it('throws with status 404 when instance was never provisioned', async () => {
326+
const { instance } = createInstance();
327+
328+
const err: Error & { status?: number } = await instance.destroy().then(
329+
() => {
330+
throw new Error('expected rejection');
331+
},
332+
(e: Error & { status?: number }) => e
333+
);
334+
335+
expect(err.message).toBe('Instance not provisioned');
336+
expect(err.status).toBe(404);
337+
});
338+
325339
it('clears all state when both Fly deletes succeed', async () => {
326340
const { instance, storage } = createInstance();
327341
await seedRunning(storage);
@@ -1246,6 +1260,22 @@ describe('alarm runs for all live statuses', () => {
12461260
});
12471261
});
12481262

1263+
describe('start: not provisioned', () => {
1264+
it('throws with status 404 when instance was never provisioned', async () => {
1265+
const { instance } = createInstance();
1266+
1267+
const err: Error & { status?: number } = await instance.start('user-1').then(
1268+
() => {
1269+
throw new Error('expected rejection');
1270+
},
1271+
(e: Error & { status?: number }) => e
1272+
);
1273+
1274+
expect(err.message).toBe('Instance not provisioned');
1275+
expect(err.status).toBe(404);
1276+
});
1277+
});
1278+
12491279
describe('startExistingMachine: transient vs 404 errors', () => {
12501280
it('does NOT recreate machine on transient 500 error', async () => {
12511281
const { instance, storage } = createInstance();
@@ -2888,6 +2918,20 @@ describe('stop: error propagation', () => {
28882918
expect(storage._store.get('status')).toBe('stopped');
28892919
expect(storage._store.get('lastStoppedAt')).toBeDefined();
28902920
});
2921+
2922+
it('throws with status 404 when instance was never provisioned', async () => {
2923+
const { instance } = createInstance();
2924+
2925+
const err: Error & { status?: number } = await instance.stop().then(
2926+
() => {
2927+
throw new Error('expected rejection');
2928+
},
2929+
(e: Error & { status?: number }) => e
2930+
);
2931+
2932+
expect(err.message).toBe('Instance not provisioned');
2933+
expect(err.status).toBe(404);
2934+
});
28912935
});
28922936

28932937
// ============================================================================

kiloclaw/src/durable-objects/kiloclaw-instance/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ export class KiloClawInstance extends DurableObject<KiloClawEnv> {
690690
}
691691

692692
if (!this.s.userId || !this.s.sandboxId) {
693-
throw new Error('Instance not provisioned');
693+
throw Object.assign(new Error('Instance not provisioned'), { status: 404 });
694694
}
695695

696696
const flyConfig = getFlyConfig(this.env, this.s);
@@ -944,7 +944,7 @@ export class KiloClawInstance extends DurableObject<KiloClawEnv> {
944944
await this.loadState();
945945

946946
if (!this.s.userId || !this.s.sandboxId) {
947-
throw new Error('Instance not provisioned');
947+
throw Object.assign(new Error('Instance not provisioned'), { status: 404 });
948948
}
949949
if (
950950
this.s.status === 'stopped' ||
@@ -990,8 +990,8 @@ export class KiloClawInstance extends DurableObject<KiloClawEnv> {
990990
async destroy(): Promise<DestroyResult> {
991991
await this.loadState();
992992

993-
if (!this.s.userId) {
994-
throw new Error('Instance not provisioned');
993+
if (!this.s.userId || !this.s.sandboxId) {
994+
throw Object.assign(new Error('Instance not provisioned'), { status: 404 });
995995
}
996996

997997
const machineUptimeMs = this.s.lastStartedAt ? Date.now() - this.s.lastStartedAt : 0;

0 commit comments

Comments
 (0)