Skip to content

Commit b9ef75e

Browse files
committed
fix(uploads): keep a missing bucket or container out of the not-found path
NoSuchBucket and ContainerNotFound also answer 404, so the status-only match read a total storage misconfiguration as an absent object — every file read would fail closed with nothing left to alert on.
1 parent f3f3607 commit b9ef75e

2 files changed

Lines changed: 28 additions & 6 deletions

File tree

apps/sim/lib/uploads/core/errors.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ describe('isObjectNotFoundError', () => {
3030
expect(isObjectNotFoundError({ name: 'Error', code: 'NoSuchKey' })).toBe(true)
3131
})
3232

33+
it('does not read a missing bucket or container as an absent object', () => {
34+
/**
35+
* These answer 404 too. Reading them as absence would turn a total storage
36+
* misconfiguration into silent fail-closed reads with nothing to alert on.
37+
*/
38+
expect(
39+
isObjectNotFoundError({ name: 'NoSuchBucket', $metadata: { httpStatusCode: 404 } })
40+
).toBe(false)
41+
expect(
42+
isObjectNotFoundError({ name: 'RestError', code: 'ContainerNotFound', statusCode: 404 })
43+
).toBe(false)
44+
})
45+
3346
it('matches on status alone when the provider sends no label', () => {
3447
expect(isObjectNotFoundError({ $metadata: { httpStatusCode: 404 } })).toBe(true)
3548
expect(isObjectNotFoundError({ statusCode: 404 })).toBe(true)

apps/sim/lib/uploads/core/errors.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
const NOT_FOUND_LABELS = new Set(['NotFound', 'NoSuchKey', 'BlobNotFound'])
1+
const OBJECT_NOT_FOUND_LABELS = new Set(['NotFound', 'NoSuchKey', 'BlobNotFound'])
2+
3+
/**
4+
* A missing bucket or container is a misconfiguration, not an absent object, and
5+
* it also answers 404. Without this it would read as "no metadata" and every file
6+
* read would fail closed with no error to alert on.
7+
*/
8+
const CONTAINER_NOT_FOUND_LABELS = new Set(['NoSuchBucket', 'ContainerNotFound'])
29

310
/**
411
* True when a storage provider reports that an object simply does not exist.
@@ -23,12 +30,14 @@ export function isObjectNotFoundError(error: unknown): boolean {
2330
}
2431

2532
/**
26-
* `name` and `code` are checked independently: Azure raises a `RestError` whose
27-
* `name` says nothing useful and whose `code` carries the reason, while the AWS
28-
* SDK puts the reason in `name`.
33+
* `name` and `code` are both consulted: Azure raises a `RestError` whose `name`
34+
* carries the class and whose `code` carries the reason, while the AWS SDK puts
35+
* the reason in `name`.
2936
*/
30-
if (typeof name === 'string' && NOT_FOUND_LABELS.has(name)) return true
31-
if (typeof code === 'string' && NOT_FOUND_LABELS.has(code)) return true
37+
const labels = [name, code].filter((value): value is string => typeof value === 'string')
38+
39+
if (labels.some((label) => CONTAINER_NOT_FOUND_LABELS.has(label))) return false
40+
if (labels.some((label) => OBJECT_NOT_FOUND_LABELS.has(label))) return true
3241

3342
return code === 404 || statusCode === 404 || $metadata?.httpStatusCode === 404
3443
}

0 commit comments

Comments
 (0)