JuliusBrussee/caveman · error · ValueError
{name} must not contain surrounding whitespace
Error message
{name} must not contain surrounding whitespace What it means
ValueError from _normalized_service_url in packages/sdk/python/caveman_cloud/core.py when a service URL passed to the Cave client has leading or trailing whitespace (value.strip() != value). This is the first, cheapest sanity check before urlsplit runs; it exists so a stray space from an env var or config file fails loudly at construction time instead of producing a mangled request URL later.
Source
Thrown at packages/sdk/python/caveman_cloud/core.py:309
# Runtime-policy constants (see the RuntimePolicyClient section at the bottom of
# this module). Declared here because Cave.runtime_policy defaults to the env name.
_POLICY_KILL_ENV = "CAVEMAN_POLICY_KILL"
_POLICY_PATH = "/sdk/v1/runtime-policy"
_POLICY_SCHEMA_VERSION = "caveman.runtime-policy.v1"
_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("/")
@dataclassView on GitHub (pinned to 27d5a3981a)
Solutions
- Strip the value before passing it: Cave(..., base_url=os.environ["CAVE_BASE_URL"].strip()).
- Fix the source: remove the stray space/newline in the .env file, CI secret, or config entry.
- Keep credentials-bearing or padded values out of the URL itself; pass them via the api_key field.
Example fix
# before base_url=os.environ["CAVE_BASE_URL"] # after base_url=os.environ["CAVE_BASE_URL"].strip()
Defensive patterns
Strategy: validation
Validate before calling
def clean_service_url(value: str) -> str:
value = value.strip()
if not value:
raise ValueError("service URL is empty")
return value
base_url = clean_service_url(os.environ["CAVE_BASE_URL"]) Prevention
- Strip env-sourced URLs at load time (one helper, used everywhere).
- Quote .env values and avoid trailing spaces/newlines in CI secret fields.
When it happens
Trigger: Constructing Cave with base_url or another service URL like " https://gateway.example.com " — commonly a value read from a .env file or shell variable that picked up a trailing newline/space, or a quoted config string with padding.
Common situations: Values loaded from dotenv/CI secrets containing trailing newlines; YAML config parsed with folded scalars adding whitespace; copy-pasting URLs with an accidental leading space.
Related errors
- {name} must be an absolute http(s) URL
- {name} must be an absolute http(s) URL without credentials
- cave_breaker_threshold_invalid
- cave_breaker_retry_backoff_invalid
- caveman agent: unknown sandbox mode ${JSON.stringify(sandbox
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/bacfe289c1ec4102.
Report an issue: GitHub.