JuliusBrussee/caveman · error · ValueError
must be an absolute http(s) URL without credentials
Error message
{name} must be an absolute http(s) URL without credentials What it means
This ValueError is raised when the URL parses and uses http(s), but either has no hostname, or embeds a username/password (`https://user:pass@host`). The SDK forbids credentials in the URL itself; authentication is done via the API key instead.
Solutions
- Remove the `user:pass@` portion from the URL; pass credentials via `api_key` instead.
- Use a plain host URL, e.g. `https://cave.example.com`.
- If you intended basic auth, switch to the SDK's authentication mechanism rather than URL credentials.
Example fix
// before cave = Cave(api_key=key, service_url="https://user:secret@cave.example.com") // after cave = Cave(api_key=key, service_url="https://cave.example.com")
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
def has_url_credentials(v: str) -> bool:
p = urlsplit(v)
return p.username is not None or p.password is not None
# reject if True; auth belongs in api_key Try / catch
try:
cave = Cave(api_key=key, service_url=url)
except ValueError as e:
raise ConfigError("remove credentials from service_url; use api_key") from e Prevention
- Never paste URLs containing '@' credentials into config
- Use the SDK's api_key for authentication
- Lint config files for user:pass@ patterns
When it happens
Trigger: Constructing the config with a URL like `https://user:pass@cave.example.com` or a scheme-relative/malformed URL that leaves `parsed.hostname` empty.
Common situations: Baking HTTP basic-auth credentials into the URL copied from another tool, or accidentally including `@` in the host portion.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- invalid_endpoint
- : every source must be HTTPS
- must be an absolute http(s) URL
- must be an absolute http(s) URL without credentials
- must not contain a query or fragment
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/4143e9fa36444da9.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/python/caveman_cloud/core.py:343
return raw
return "unlabeled-workflow"
def _normalized_service_url(value: str, name: str) -> str:
if value.strip() != value:
raise ValueError(f"{name} must not contain surrounding whitespace")
try:
parsed = urlsplit(value)
port = parsed.port
except (TypeError, ValueError) as error:
raise ValueError(f"{name} must be an absolute http(s) URL") from error
if (
parsed.scheme not in ("http", "https")
or not parsed.hostname
or parsed.username is not None
or parsed.password is not None
):
raise ValueError(f"{name} must be an absolute http(s) URL without credentials")
if parsed.query or parsed.fragment:
raise ValueError(f"{name} must not contain a query or fragment")
return value.rstrip("/")
@dataclass
class Cave:
api_key: str
base_url: str
agent: str
# CAVE_WORKFLOW lets a wrapper (`cave wrap --workflow x`) label every request
# from an SDK app without a code change. An explicit value always wins. The
# env value is normalized to the gateway's label rule (lowercase [a-z0-9_-],
# max 96); an invalid ambient value is ignored rather than 400-ing every
# request. Mirrors @caveman-ai/sdk (TypeScript).
default_workflow: str = field(default_factory=lambda: _env_workflow())
retention: str = "metadata"
verify_on_init: bool = FalseView on GitHub (pinned to 3ee70a1026)