hcengineering/platform · error · StorageError

await response.text()

Error message

await response.text()

What it means

When the upload POST completes but the server responds with a non-ok status, put() reads the response body text and throws it as a StorageError. The message is whatever error text the upload service returned (HTML error pages, JSON error bodies, or plain text like 'Unauthorized').

Source

Thrown at foundations/core/packages/api-client/src/storage/client.ts:118

  }

  async put (objectName: string, stream: Readable | Buffer | string, contentType: string, size?: number): Promise<Blob> {
    const buffer = await toBuffer(stream)
    const file = new File([new Uint8Array(buffer)], objectName, { type: contentType })
    const formData = new FormData()
    formData.append('file', file)
    let response
    try {
      response = await fetch(this.uploadUrl, {
        method: 'POST',
        body: formData,
        headers: { ...this.headers }
      })
    } catch (error: any) {
      throw new NetworkError(`Network error ${error}`)
    }
    if (!response.ok) {
      throw new StorageError(await response.text())
    }
    const result = (await response.json()) as BlobUploadResult[]
    if (Object.hasOwn(result[0], 'id')) {
      const fileResult = result[0] as BlobUploadSuccess
      return {
        _class: core.class.Blob,
        _id: fileResult.id as Ref<Blob>,
        space: core.space.Configuration,
        modifiedOn: fileResult.metadata.lastModified,
        modifiedBy: core.account.System,
        provider: '',
        contentType: fileResult.metadata.contentType,
        etag: fileResult.metadata.etag,
        version: null,
        size: fileResult.metadata.size
      }
    } else {
      const error = (result[0] as BlobUploadError) ?? 'Unknown error'

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Log the StorageError message — it contains the server's error body which names the cause (Unauthorized, Payload Too Large, etc.).
  2. Regenerate the workspace token and recreate the storage client via connectStorage.
  3. Check UPLOAD_URL in server config matches the running upload service route.
  4. Reduce file size or raise proxy/upload body-size limits for 413 errors.
  5. Verify Authorization header reaches the service (not stripped by a proxy).

Example fix

// before
await storage.put(name, stream, contentType) // StorageError: <html>401...</html>
// after
try {
  await storage.put(name, stream, contentType)
} catch (err) {
  if (err instanceof StorageError && /unauthorized/i.test(err.message)) {
    storage = await connectStorage(url, options) // fresh token
    return storage.put(name, stream, contentType)
  }
  throw err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure a fresh token before constructing/using the storage client
if (Date.now() >= tokenExpiresAt - 60_000) {
  token = (await getWorkspaceToken(url, options, config)).token
  storage = createStorageClient(filesUrl, uploadUrl, token, workspace)
}

Type guard

function isStorageError(e: unknown): e is StorageError {
  return e instanceof StorageError
}

Try / catch

try {
  return await storage.put(name, stream, contentType)
} catch (e) {
  if (isStorageError(e)) {
    // message contains the server response text, e.g. 'Unauthorized' or 'Payload Too Large'
    if (/unauthorized|forbidden/i.test(e.message)) {
      // re-authenticate and retry once
    }
  }
  throw e
}

Prevention

When it happens

Trigger: Calling storageClient.put(...) and receiving 401 (invalid workspace token), 403, 404 (bad UPLOAD_URL path), 413 (payload too large), or 5xx from the upload service.

Common situations: Expired or wrongly-scoped bearer token in the StorageClient; upload service rejecting files above a size limit set at the proxy; UPLOAD_URL path changed in server config so requests hit a 404 page; auth middleware returning HTML login pages.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/019cf0b1c27beb81. Report an issue: GitHub.