goharbor/harbor · error · Exception
log level must be one of debug, info, warning, error, fatal
Error message
log level must be one of debug, info, warning, error, fatal
What it means
Thrown by parse_yaml_config() in make/photon/prepare/utils/configs.py when harbor.yml's 'log.level' is not one of the literal strings debug, info, warning, error, fatal. The membership test runs on the raw value before log_level.lower() is applied, so the check is case-sensitive: 'INFO' or 'Info' fail even though they look correct. 'warn' (a common shorthand elsewhere) also fails — Harbor requires 'warning'.
Source
Thrown at make/photon/prepare/utils/configs.py:247
value = config_dict["max_job_duration_hours"]
if not isinstance(value, int) or value < 24:
config_dict["max_job_duration_hours"] = 24
config_dict['job_loggers'] = js_config["job_loggers"]
config_dict['logger_sweeper_duration'] = js_config["logger_sweeper_duration"]
config_dict['jobservice_secret'] = generate_random_string(16)
# notification config
notification_config = configs.get('notification') or {}
config_dict['notification_webhook_job_max_retry'] = notification_config["webhook_job_max_retry"]
config_dict['notification_webhook_job_http_client_timeout'] = notification_config["webhook_job_http_client_timeout"]
# Log configs
allowed_levels = ['debug', 'info', 'warning', 'error', 'fatal']
log_configs = configs.get('log') or {}
log_level = log_configs['level']
if log_level not in allowed_levels:
raise Exception('log level must be one of debug, info, warning, error, fatal')
config_dict['log_level'] = log_level.lower()
# parse local log related configs
local_logs = log_configs.get('local') or {}
if local_logs:
config_dict['log_location'] = local_logs.get('location') or '/var/log/harbor'
config_dict['log_rotate_count'] = local_logs.get('rotate_count') or 50
config_dict['log_rotate_size'] = local_logs.get('rotate_size') or '200M'
# parse external log endpoint related configs
if log_configs.get('external_endpoint'):
config_dict['log_external'] = True
config_dict['log_ep_protocol'] = log_configs['external_endpoint']['protocol']
config_dict['log_ep_host'] = log_configs['external_endpoint']['host']
config_dict['log_ep_port'] = log_configs['external_endpoint']['port']
else:
config_dict['log_external'] = False
View on GitHub (pinned to 7b2fd08cc5)
Solutions
- Set the level to an exact lowercase value from the allowed set, e.g. 'info' or 'debug'
- Use 'warning' instead of 'warn'
- Re-run ./install.sh
Example fix
# harbor.yml (before) log: level: INFO # harbor.yml (after) log: level: info
Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = {'debug', 'info', 'warning', 'error', 'fatal'}
level = (cfg.get('log') or {}).get('level', 'info')
if level not in ALLOWED:
raise SystemExit('log.level %r must be one of %s (lowercase)' % (level, sorted(ALLOWED))) Prevention
- harbor.yml log levels are lowercase-only: debug, info, warning, error, fatal
- 'warn' is not accepted - write 'warning'
- When templating harbor.yml, normalize injected log levels with | lower
When it happens
Trigger: harbor.yml contains log: level: INFO (uppercase), level: warn, or a typo like 'eror'. The comparison log_level not in ['debug','info','warning','error','fatal'] is exact-match on the raw YAML string.
Common situations: Values copied from docker logging drivers or logrus examples that use uppercase or 'warn'; editing generated configs where other sections accept mixed case; template variables injecting 'DEBUG' from CI environment variables.
Related errors
- Protocol in external log endpoint must be one of 'udp' or 't
- Please specify hostname
- Error: must set log endpoint host to enable external host
- Error: must set log endpoint port to enable external host
- Error: storage driver %s is not supported, only the followin
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/71c9c43d77437167.
Report an issue: GitHub.