hcengineering/platform · error · NotFoundError
text (response body)
Error message
text (response body)
What it means
wrappedFetch in the storage client converts failed HTTP responses into typed errors. When the server responds with a non-ok status, the response body text is captured and thrown: NotFoundError for 404, StorageError for any other failure. The error message is the raw response body, so the actual reason lives in the server's response text.
Source
Thrown at foundations/core/packages/api-client/src/storage/client.ts:192
chunks.push(chunk)
}
return Buffer.concat(chunks as any)
} else {
throw new TypeError('Unsupported data type')
}
}
async function wrappedFetch (url: string | URL, init?: RequestInit): Promise<Response> {
let response: Response
try {
response = await fetch(url, init)
} catch (error: any) {
throw new NetworkError(`Network error ${error}`)
}
if (!response.ok) {
const text = await response.text()
if (response.status === 404) {
throw new NotFoundError(text)
} else {
throw new StorageError(text)
}
}
return response
}
export function createStorageClient (
filesUrl: string,
uploadUrl: string,
token: string,
workspace: WorkspaceUuid
): StorageClient {
return new StorageClientImpl(filesUrl, uploadUrl, token, workspace)
}
export async function connectStorage (url: string, options: AuthOptions, config?: ServerConfig): Promise<StorageClient> {
config ??= await loadServerConfig(url)View on GitHub (pinned to 63e28dc964)
Solutions
- Log the error message — it is the raw response body — to see the server's actual reason.
- For 404: verify the blob/document ID exists before calling stat/response/remove, or treat NotFoundError as an expected miss.
- Check the storage endpoint URL and credentials used to create the storage client.
- Retry with backoff for 5xx statuses, as these usually indicate transient storage-service problems.
- Capture response.status/text in the error for observability instead of discarding the status code.
Example fix
// before
try {
await storage.stat(blobId)
} catch (err) {
console.error('failed', err) // message is opaque body text
}
// after
import { NotFoundError } from '@hcengineering/api-client'
try {
await storage.stat(blobId)
} catch (err) {
if (err instanceof NotFoundError) return null // expected miss
console.error('storage status/body:', err.message)
throw err
} Defensive patterns
Strategy: try-catch
Validate before calling
// optionally probe before use
const res = await fetch(`${storageUrl}/${blobId}`, { method: 'HEAD' })
if (res.status === 404) return null Type guard
function isNotFound(err: unknown): err is NotFoundError {
return err instanceof NotFoundError
} Try / catch
try {
return await storage.stat(blobId)
} catch (err) {
if (err instanceof NotFoundError) return null
if (err instanceof StorageError) console.error('storage:', err.message)
throw err
} Prevention
- Always treat NotFoundError as an expected outcome, not a crash
- Log err.message — it is the server's response body
- Verify blob IDs exist before storage operations in UI flows
- Retry only on non-404 StorageError with backoff
When it happens
Trigger: Any storage API call (stat, response, remove) that reaches wrappedFetch and receives response.ok === false: missing blobs (404 -> NotFoundError), permission denied, malformed requests, or storage-server outages (other statuses -> StorageError).
Common situations: Requesting a blob ID that was deleted or never uploaded; wrong storage endpoint URL in config; expired or missing auth token causing 401/403; storage service temporarily down returning 5xx.
Related errors
- Missing response body
- Failed to delete file
- Failed to delete file
- Failed to delete file
- response.statusText
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/6ea6df3152f9fab0.
Report an issue: GitHub.