{"record":{"id":"bacfe289c1ec4102","repo":"JuliusBrussee/caveman","slug":"name-must-not-contain-surrounding-whitespace-bacfe2","errorCode":null,"errorMessage":"{name} must not contain surrounding whitespace","messagePattern":"(.+?) must not contain surrounding whitespace","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/sdk/python/caveman_cloud/core.py","lineNumber":309,"sourceCode":"# Runtime-policy constants (see the RuntimePolicyClient section at the bottom of\n# this module). Declared here because Cave.runtime_policy defaults to the env name.\n_POLICY_KILL_ENV = \"CAVEMAN_POLICY_KILL\"\n_POLICY_PATH = \"/sdk/v1/runtime-policy\"\n_POLICY_SCHEMA_VERSION = \"caveman.runtime-policy.v1\"\n_POLICY_MAX_RESPONSE_BYTES = 1024 * 1024\n\n\ndef _env_workflow() -> str:\n    \"\"\"CAVE_WORKFLOW normalized to the gateway label rule, else the honest default.\"\"\"\n    raw = (os.environ.get(\"CAVE_WORKFLOW\") or \"\").lower()\n    if raw and len(raw) <= 96 and all(c in _WORKFLOW_CHARS for c in raw):\n        return raw\n    return \"unlabeled-workflow\"\n\n\ndef _normalized_service_url(value: str, name: str) -> str:\n    if value.strip() != value:\n        raise ValueError(f\"{name} must not contain surrounding whitespace\")\n    try:\n        parsed = urlsplit(value)\n        port = parsed.port\n    except (TypeError, ValueError) as error:\n        raise ValueError(f\"{name} must be an absolute http(s) URL\") from error\n    if (\n        parsed.scheme not in (\"http\", \"https\")\n        or not parsed.hostname\n        or parsed.username is not None\n        or parsed.password is not None\n    ):\n        raise ValueError(f\"{name} must be an absolute http(s) URL without credentials\")\n    if parsed.query or parsed.fragment:\n        raise ValueError(f\"{name} must not contain a query or fragment\")\n    return value.rstrip(\"/\")\n\n\n@dataclass","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/sdk/python/caveman_cloud/core.py#L291-L327","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nbase_url=os.environ[\"CAVE_BASE_URL\"]\n# after\nbase_url=os.environ[\"CAVE_BASE_URL\"].strip()","handlingStrategy":"validation","validationCode":"def clean_service_url(value: str) -> str:\n    value = value.strip()\n    if not value:\n        raise ValueError(\"service URL is empty\")\n    return value\n\nbase_url = clean_service_url(os.environ[\"CAVE_BASE_URL\"])","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Strip env-sourced URLs at load time (one helper, used everywhere).","Quote .env values and avoid trailing spaces/newlines in CI secret fields."],"tags":["python","sdk","url","validation","configuration"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}