Billionmail/BillionMail · error

invalid timezone

Error message

invalid timezone

What it means

validateConfigValue checks the TZ/timezone key with public.IsValidTimezone and rejects values that are not IANA timezone identifiers. The timezone is propagated into container and service configs, so an unknown value would cause time-dependent features (logs, scheduling, warmup) to misbehave.

Source

Thrown at core/internal/controller/settings/settings.go:228

	case "SMTP_PORT", "SMTPS_PORT", "SUBMISSION_PORT", "IMAP_PORT", "IMAPS_PORT", "POP_PORT", "POPS_PORT", "HTTP_PORT", "HTTPS_PORT", "REDIS_PORT",
		"smtp", "smtps", "submission", "imap", "imaps", "pop", "pops", "http", "https", "redis_port":
		// Port: 1-65535
		port := public.ParseInt(value)
		if port < 1 || port > 65535 {
			return fmt.Errorf("port must be between 1-65535")
		}

	case "IPV4_NETWORK", "ipv4_network":
		// IPv4 network: CIDR format
		if !public.IsValidCIDR(value) {
			return fmt.Errorf("IPv4 network format is incorrect, please use CIDR format (e.g. 192.168.1.0/24)")
		}

	case "TZ", "timezone":
		// Timezone: check if it is a valid timezone
		if !public.IsValidTimezone(value) {
			return fmt.Errorf("invalid timezone")
		}

	case "FAIL2BAN_INIT", "fail2ban":
		// fail2ban: only allowed y/n or 1/0
		if value != "y" && value != "n" && value != "1" && value != "0" {
			return fmt.Errorf("fail2ban value can only be y/n or 1/0")
		}
	case "RETENTION_DAYS", "retention_days":
		// retention_days: must be a number
		if _, err := strconv.Atoi(value); err != nil {
			return fmt.Errorf("retention_days must be a number")
		}
	}

	// General character check: not allowed dangerous characters
	if public.ContainsDangerousChars(value) {
		return fmt.Errorf("configuration value contains illegal characters")
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Use an IANA timezone identifier, e.g. 'America/New_York' or 'Europe/Berlin'
  2. Check the name against the IANA tz database (or Go's time.LoadLocation) before saving
  3. Replace fixed-offset strings like 'UTC+2' with the corresponding zone name
  4. Trim whitespace and fix casing to match the canonical zone name

Example fix

// before
TZ = EST
// after
TZ = America/New_York
Defensive patterns

Strategy: validation

Validate before calling

function isValidTimezone(tz: string): boolean {
  try { Intl.DateTimeFormat(undefined, { timeZone: tz }); return true; }
  catch { return false; }
}
if (!isValidTimezone(tz)) throw new Error('invalid timezone');

Type guard

function isIANAZone(v: string): v is string {
  return /^[A-Za-z]+\/[A-Za-z_+-]+$/.test(v) || v === 'UTC';
}

Try / catch

try {
  await api.setSystemConfigKey('TZ', tz);
} catch (e) {
  if (String(e.message).includes('invalid timezone')) {
    notify('Use an IANA timezone name like Europe/Berlin');
  } else throw e;
}

Prevention

When it happens

Trigger: Setting TZ to abbreviations ('EST', 'CET'), offsets ('UTC+2'), a made-up zone ('Europe/Berlin '), wrong casing ('america/new_york' if the checker is case-sensitive), or a zone not present in the tz database.

Common situations: Admins copy Windows timezone names ('W. Europe Standard Time') or GMT offsets from other systems instead of IANA names like 'Europe/Berlin' or 'America/New_York'.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/f02f1a8cbe43831d. Report an issue: GitHub.