hcengineering/platform · error · NetworkError

Network error ${error}

Error message

Network error ${error}

What it means

StorageClientImpl.put wraps its raw fetch to uploadUrl in try/catch; any exception thrown by fetch itself (DNS failure, connection refused/reset, TLS error, abort) is rethrown as NetworkError(`Network error ${error}`). Note this wraps only transport-level failures — HTTP error statuses are handled separately.

Source

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

      throw new StorageError('Missing response body')
    }
    return Readable.from(response.body)
  }

  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

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Verify uploadUrl is reachable: curl -X POST the upload endpoint from the same network.
  2. Check config.UPLOAD_URL resolution in connectStorage (it must be absolute or a path concatenated with the base URL, with :workspace substituted).
  3. Inspect the inner error text in NetworkError message (ECONNREFUSED, ENOTFOUND, etc.) and fix infrastructure accordingly.
  4. Add retry with backoff for transient connection errors around put().

Example fix

// before
const blob = await storage.put(name, stream, contentType)
// after
let blob
for (let attempt = 0; attempt < 3; attempt++) {
  try {
    blob = await storage.put(name, stream, contentType)
    break
  } catch (err) {
    if (err instanceof NetworkError && attempt < 2) {
      await new Promise(r => setTimeout(r, 2 ** attempt * 500))
      continue
    }
    throw err
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// Validate upload URL before uploading
const u = new URL(uploadUrl)
if (!/^https?:$/.test(u.protocol)) throw new Error(`Bad upload URL: ${uploadUrl}`)
// optionally: await fetch(uploadUrl, { method: 'OPTIONS' }) as a reachability probe

Type guard

function isNetworkError(e: unknown): e is NetworkError {
  return e instanceof NetworkError
}

Try / catch

try {
  return await storage.put(name, stream, contentType)
} catch (e) {
  if (isNetworkError(e)) {
    // transient transport failure — retry with backoff
    await sleep(1000)
    return storage.put(name, stream, contentType)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling storageClient.put(...) when the upload URL is unreachable: wrong UPLOAD_URL config, upload service down, DNS failure, connection reset mid-upload of large multipart bodies, or TLS certificate problems.

Common situations: UPLOAD_URL not set or pointing at localhost in a container; k8s service name typo; oversized uploads hitting proxy body-size limits that kill the connection; transient network blips between services.

Related errors


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