hcengineering/platform · error · ApiError

Both width and height must be provided

Error message

Both width and height must be provided

What it means

The viewport parameters are all-or-nothing: if exactly one of `width` or `height` is present in the query string, parsePrintOptions throws this 400 ApiError. A viewport needs both dimensions to be meaningful for the headless browser.

Source

Thrown at services/print/pod-print/src/server.ts:169

  if (orientation !== undefined && !validPageOrientations.includes(orientation as any)) {
    throw new ApiError(400, `Invalid page orientation: ${orientation}`)
  }

  const rawWidth = (query.width ?? '') as string
  const rawHeight = (query.height ?? '') as string

  let viewport: PrintOptions['viewport'] | undefined
  if (rawWidth.length > 0 && rawHeight.length > 0) {
    viewport = {
      width: parseInt(rawWidth, 10),
      height: parseInt(rawHeight, 10)
    }

    if (Number.isNaN(viewport.width) || Number.isNaN(viewport.height)) {
      throw new ApiError(400, 'Invalid width or height')
    }
  } else if (rawWidth.length > 0 || rawHeight.length > 0) {
    throw new ApiError(400, 'Both width and height must be provided')
  }

  return { kind, orientation, viewport }
}

export function createServer (
  storageConfig: StorageConfiguration,
  allowedHostnames: string[]
): { app: Express, close: () => void } {
  const storageAdapter = buildStorageFromConfig(storageConfig)
  const measureCtx = initStatisticsContext('print', {
    factory: () =>
      createOpenTelemetryMetricsContext(
        'print',
        {},
        {},
        newMetrics(),
        new SplitLogger('print', {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Always send both width and height together, e.g. &width=1280&height=720.
  2. Or send neither — omit both to use defaults.
  3. Fix the client URL builder so the two parameters are added as a unit.
  4. Sanitize empty strings to undefined before deciding whether to append the params.

Example fix

// before
params.set('width', width ?? '') // may yield lone width=
// after
if (width != null && height != null) {
  params.set('width', String(width))
  params.set('height', String(height))
}
Defensive patterns

Strategy: validation

Validate before calling

function appendViewport (params: URLSearchParams, width?: number, height?: number): void {
  if ((width == null) !== (height == null)) {
    throw new Error('width and height must be provided together')
  }
  if (width != null && height != null) {
    params.set('width', String(width))
    params.set('height', String(height))
  }
}

Try / catch

try {
  const res = await fetch(url)
  if (!res.ok) {
    const body = await res.json()
    if (body.code === 400 && /Both width and height/.test(body.message)) {
      // either add the missing dimension or drop both and retry
    }
    throw new Error(body.message)
  }
  return await res.json()
} catch (err) { /* handle */ }

Prevention

When it happens

Trigger: GET /print?link=...&width=1280 (height missing), or width= (empty value counts as absent) paired with height=600. Also happens when URL builders drop empty parameters asymmetrically.

Common situations: Copy-pasting an example URL and deleting one parameter; conditional client code that appends width but only conditionally appends height; a serializer that omits undefined height but keeps width.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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