JuliusBrussee/caveman · error · ValueError
must not contain surrounding whitespace
Error message
{name} must not contain surrounding whitespace What it means
`_normalized_service_url` validates a service URL field (e.g. a `service_url` dataclass attribute) when the config object is instantiated via `__post_init__`. The library requires callers to pass pre-trimmed URLs; leading/trailing whitespace would otherwise silently change routing or auth signing. It throws this ValueError when `value.strip() != value`.
Solutions
- Call `.strip()` on the URL before constructing the object.
- Fix the source of the whitespace (trim when reading env vars or files).
- Set the URL directly as a literal with no surrounding whitespace.
Example fix
// before service_url = os.environ["CAVE_SERVICE_URL"] cave = Cave(api_key=key, service_url=service_url) // after service_url = os.environ["CAVE_SERVICE_URL"].strip() cave = Cave(api_key=key, service_url=service_url)
Defensive patterns
Strategy: validation
Validate before calling
def valid_service_url(v: str) -> bool:
return isinstance(v, str) and v.strip() == v and bool(v) Try / catch
try:
cave = Cave(api_key=key, service_url=url)
except ValueError as e:
logger.error("invalid service url: %s", e)
raise ConfigError(str(e)) from e Prevention
- Always .strip() URLs read from env vars or files
- Add the trim in your config-loading layer, not at call sites
- Fail fast at startup with a clear message if the URL is malformed
When it happens
Trigger: Constructing a config/dataclass (e.g. `Cave` or a service settings object) whose URL attribute is read from an env var, YAML/JSON config, or CLI input that contains a leading or trailing space, tab, or newline (common with `readline()` on files).
Common situations: Reading `CAVE_SERVICE_URL` from an environment variable with a stray space, appending a newline from a file read without `.strip()`, or copy-pasting a URL with a trailing blank.
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
- must be an absolute http(s) URL
- must be an absolute http(s) URL without credentials
- must not contain a query or fragment
- artifact_id is required
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/bacfe289c1ec4102.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/python/caveman_cloud/core.py:331
# 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 3ee70a1026)