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
- 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.
- If the value comes from req.query, take the first element of a possible array and verify it is a string.
- Fix the upstream config so the tz field holds a timezone name string like 'UTC'.
- 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
- Never pass req.query values directly; normalize arrays and ParsedQs first
- Validate config objects with a schema (zod/joi) enforcing string|null timezone fields
- Avoid parseInt/toNumber on timezone values
- Keep TS types honest at boundaries with runtime checks
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
- Value "${raw}" is not valid for CUBEJS_SCHEDULED_REFRESH_TIM
- Value "${value}" is not valid for CUBEJS_DEFAULT_TIMEZONE. M
- The ${keyByDataSource('CUBEJS_DB_USE_SELECT_TEST_CONNECTION'
- options.timestampPrecision is required, actual: ${options.ti
- Unknown timezone: ${timezone}
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/cef7ce5b0b20e9ca.
Report an issue: GitHub.