JuliusBrussee/caveman · error · ValueError
{name} must be an absolute http(s) URL
Error message
{name} must be an absolute http(s) URL What it means
ValueError from _normalized_service_url in packages/sdk/python/caveman_cloud/core.py, raised from the urlsplit failure handler. Accessing parsed.port triggers Python's port parsing, which raises ValueError for malformed ports (non-numeric, out of range, or empty); TypeError propagates the same way for non-string input. The message says the value must be an absolute http(s) URL because that is the accepted shape, even though the proximate cause is usually a bad port segment.
Source
Thrown at packages/sdk/python/caveman_cloud/core.py:314
_POLICY_MAX_RESPONSE_BYTES = 1024 * 1024
def _env_workflow() -> str:
"""CAVE_WORKFLOW normalized to the gateway label rule, else the honest default."""
raw = (os.environ.get("CAVE_WORKFLOW") or "").lower()
if raw and len(raw) <= 96 and all(c in _WORKFLOW_CHARS for c in raw):
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 requestView on GitHub (pinned to 27d5a3981a)
Solutions
- Fix the URL so the port is a valid integer between 1 and 65535, or omit the port entirely for the default scheme port.
- If building the URL dynamically, only append :port when the value is set.
- Ensure the value is a str before passing it to Cave.
Example fix
# before
base_url = f"https://{host}:{port}" # port is None -> ':None'
# after
base_url = f"https://{host}:{port}" if port else f"https://{host}" Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
def parse_port(value: str) -> int | None:
try:
urlsplit(value).port
return True
except (TypeError, ValueError):
return False
if not parse_port(base_url):
raise ValueError(f"base_url has a malformed port: {base_url!r}") Prevention
- Only append ':port' when the port variable is a real integer.
- Validate user-supplied base URLs at config load, not at first request.
When it happens
Trigger: A URL like "https://gateway.example.com:abc/" (non-numeric port), "https://host:99999/" (out-of-range port), "https://host:/" (empty port), or passing None/an int instead of a string where a URL is expected.
Common situations: Composing base_url from parts with an unset or placeholder port variable (f"https://{host}:{port}" with port=None rendering as 'None'); templating mistakes in deployment config; typos after the last colon.
Related errors
- {name} must not contain surrounding whitespace
- {name} must be an absolute http(s) URL without credentials
- artifactId is required
- retry loop interrupted: tool call {signature!r} repeated {re
- compat upstream %q: base_url: %w
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/6f00fc34e6a500ff.
Report an issue: GitHub.