{"record":{"id":"80c9a57507e91bbf","repo":"goharbor/harbor","slug":"bad-redis-url-for-registry-s","errorCode":null,"errorMessage":"bad redis url for registry:%s","messagePattern":"bad redis url for registry:(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"make/photon/prepare/utils/registry.py","lineNumber":89,"sourceCode":"        return {\n            'sentinel_master_set': u.path.split('/')[1],\n            'redis_host': u.netloc.split('@')[-1],\n            'redis_password': '' if u.password is None else unquote(u.password),\n            'redis_username': '' if u.username is None else unquote(u.username),\n            'redis_db_index_reg': len(u.path.split('/')) == 3 and int(u.path.split('/')[2]) or 0,\n            'redis_enableTLS': 'false',\n        }\n    elif u.scheme == 'rediss+sentinel':\n        return {\n            'sentinel_master_set': u.path.split('/')[1],\n            'redis_host': u.netloc.split('@')[-1],\n            'redis_password': '' if u.password is None else unquote(u.password),\n            'redis_username': '' if u.username is None else unquote(u.username),\n            'redis_db_index_reg': len(u.path.split('/')) == 3 and int(u.path.split('/')[2]) or 0,\n            'redis_enableTLS': 'true',\n        }\n    else:\n        raise Exception('bad redis url for registry:' + redis_url)\n\ndef get_storage_provider_info(provider_name, provider_config):\n    provider_config_copy = copy.deepcopy(provider_config)\n    if provider_name == \"filesystem\":\n        if not (provider_config_copy and ('rootdirectory' in provider_config_copy)):\n            provider_config_copy['rootdirectory'] = '/storage'\n    if provider_name == 'gcs' and provider_config_copy.get('keyfile'):\n        provider_config_copy['keyfile'] = '/etc/registry/gcs.key'\n    # generate storage configuration section in yaml format\n    storage_provider_conf_list = [provider_name + ':']\n    for config in provider_config_copy.items():\n        if config[1] is None:\n            value = ''\n        elif config[1] == True:\n            value = 'true'\n        else:\n            value = config[1]\n        storage_provider_conf_list.append('{}: {}'.format(config[0], value))","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/make/photon/prepare/utils/registry.py#L71-L107","documentation":"Thrown by parse_redis() in make/photon/prepare/utils/registry.py when the registry redis URL's scheme is not one of redis, rediss, redis+sentinel, rediss+sentinel. The function maps the URL into registry config keys (host, password, db index, TLS flag, sentinel master set); an unknown scheme means it cannot interpret the URL at all. Normally redis_url_reg is generated by get_redis_configs() from harbor.yml's redis/external_redis sections (scheme derived from sentinel_master_set and tlsOptions), so this raise points at a malformed or externally injected URL string.","triggerScenarios":"prepare_registry(config_dict) is called with config_dict['redis_url_reg'] that urlsplit cannot parse into a known scheme: leading/trailing whitespace (' redis://...' yields an empty/odd scheme), a typo like 'redissentinel://' or 'redis-sentinel://', or a completely missing '//' separator. Happens with custom tooling that builds the URL by hand instead of using configs.get_redis_configs().","commonSituations":"Scripts that wrap prepare and hand-assemble redis URLs; copy-paste from Redis/Sentinel docs that use different scheme spellings; YAML values with stray spaces after templating; older configs using 'redis+sentinel' variants with wrong separators.","solutions":["Use exactly one of the four schemes: redis:// (plain), rediss:// (TLS), redis+sentinel:// (sentinel), rediss+sentinel:// (sentinel+TLS)","Prefer generating the URL with utils.configs.get_redis_url()/get_redis_configs() so the scheme is derived correctly from harbor.yml","Strip whitespace: redis_url = redis_url.strip() before passing it in","For sentinel URLs include the master name in the path: redis+sentinel://user:pass@host1:26379,host2:26379/mymaster/1","Re-run prepare"],"exampleFix":"# hand-built dict (before)\ncfg['redis_url_reg'] = 'redis-sentinel://sentinel1:26379/mymaster/1'\n\n# after\nfrom utils.configs import get_redis_configs\ncfg.update(get_redis_configs(external_redis={\n    'host': 'sentinel1:26379,sentinel2:26379',\n    'sentinel_master_set': 'mymaster',\n    'registry_db_index': 1,\n    'password': 'pass'}))  # yields redis+sentinel://...","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\nALLOWED = {'redis', 'rediss', 'redis+sentinel', 'rediss+sentinel'}\nu = urlsplit(redis_url.strip())\nif u.scheme not in ALLOWED:\n    raise SystemExit('bad redis url scheme %r; allowed: %s' % (u.scheme, sorted(ALLOWED)))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Let Harbor build redis URLs from harbor.yml instead of hand-writing them","Remember TLS is 'rediss', sentinel is '+sentinel' - no hyphens","Trim YAML/template values that flow into connection URLs"],"tags":["harbor","redis","cache","configuration","url-parsing"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}