cube-js/cube · error · TypeError

Timezone must be a string, got ${typeof value}

Error message

Timezone must be a string, got ${typeof value}

What it means

canonicalTimezone in packages/cubejs-backend-shared/src/timezone.ts normalizes a timezone value to its canonical IANA zone name. Because it is called with untyped inputs (from config, requests, env), it guards its `string | null | undefined` contract at runtime: any non-string, non-nullish value (number, boolean, object) throws a TypeError naming the actual typeof.

Source

Thrown at packages/cubejs-backend-shared/src/timezone.ts:15

import moment from 'moment-timezone';

/**
 * Resolves `value` to a canonical tz-database zone name, matched case-insensitively,
 * or `null` when it is unset or not a known zone.
 *
 * @throws {TypeError} when `value` is an empty string, or is neither a string nor unset.
 */
export function canonicalTimezone(value?: string | null): string | null {
  if (value === undefined || value === null) {
    return null;
  }

  if (typeof value !== 'string') {
    throw new TypeError(`Timezone must be a string, got ${typeof value}`);
  }

  if (value === '') {
    throw new TypeError('Timezone must not be empty');
  }

  const zone = moment.tz.zone(value);

  return zone ? zone.name : null;
}

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Ensure the caller passes a string (or null/undefined for 'no timezone') — coerce with String(value) only if the value is genuinely a timezone name.
  2. If the value comes from req.query, take the first element of a possible array and verify it is a string.
  3. Fix the upstream config so the tz field holds a timezone name string like 'UTC'.
  4. Wrap the call in a validation helper that rejects non-string values with a domain-specific error.

Example fix

// before
canonicalTimezone(req.query.tz); // may be string[] → TypeError

// after
const raw = Array.isArray(req.query.tz) ? req.query.tz[0] : req.query.tz;
canonicalTimezone(typeof raw === 'string' ? raw : null);
Defensive patterns

Strategy: type-guard

Validate before calling

function toStringOrNull(v: unknown): string | null {
  if (v === undefined || v === null) return null;
  return typeof v === 'string' ? v : null; // reject non-strings instead of coercing
}

Type guard

function isTimezoneInput(v: unknown): v is string | null | undefined {
  return v === undefined || v === null || typeof v === 'string';
}

Try / catch

try {
  const tz = canonicalTimezone(raw);
} catch (e) {
  if (e instanceof TypeError && /Timezone must be a string/.test(e.message)) {
    throw new ConfigError(`timezone config must be a string, got ${typeof raw}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: canonicalTimezone(123), canonicalTimezone({ tz: 'UTC' }), canonicalTimezone(true) — any caller bypassing TypeScript types, e.g. passing req.query.tz (string[] | string | ParsedQs) or a parsed number from config.

Common situations: Express query params (arrays under ?tz=a&tz=b), JSON config where the tz field is accidentally a number/boolean, `parseInt` on a zone offset passed instead of the name.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/cef7ce5b0b20e9ca. Report an issue: GitHub.