{"record":{"id":"4143e9fa36444da9","repo":"JuliusBrussee/caveman","slug":"name-must-be-an-absolute-http-s-url-without-cre","errorCode":null,"errorMessage":"{name} must be an absolute http(s) URL without credentials","messagePattern":"(.+?) must be an absolute http\\(s\\) URL without credentials","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/sdk/python/caveman_cloud/core.py","lineNumber":321,"sourceCode":"        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\nclass Cave:\n    api_key: str\n    base_url: str\n    agent: str\n    # CAVE_WORKFLOW lets a wrapper (`cave wrap --workflow x`) label every request\n    # from an SDK app without a code change. An explicit value always wins. The\n    # env value is normalized to the gateway's label rule (lowercase [a-z0-9_-],\n    # max 96); an invalid ambient value is ignored rather than 400-ing every\n    # request. Mirrors @caveman-ai/sdk (TypeScript).\n    default_workflow: str = field(default_factory=lambda: _env_workflow())\n    retention: str = \"metadata\"\n    verify_on_init: bool = False","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/sdk/python/caveman_cloud/core.py#L303-L339","documentation":"ValueError from _normalized_service_url in packages/sdk/python/caveman_cloud/core.py, raised when urlsplit succeeds but the URL is still not acceptable: the scheme is not http/https (ftp:, file:, empty), there is no hostname (scheme-only strings like \"https://\"), or the URL embeds credentials (user:pass@host). The SDK wants credentials supplied via the api_key field, never inline in the URL, where they would leak into logs and error messages.","triggerScenarios":"Passing \"gateway.example.com\" (no scheme, so parsed.scheme is empty), \"ftp://host\", \"https://\" with no host, or \"https://user:secret@gateway.example.com\" to Cave construction for base_url or any other service URL.","commonSituations":"Forgetting the https:// prefix on a hostname from config; environments where the base URL was stored without scheme; attempting to pass an api key in the URL out of habit from other clients; scheme values with uppercase or trailing colon typos.","solutions":["Normalize to an absolute URL with scheme and host, e.g. \"https://gateway.example.com\", no trailing slash needed (the function strips it).","Move any user:pass@ credentials out of the URL and into the api_key parameter.","Add the https:// prefix when reading bare hostnames from configuration."],"exampleFix":"# before\nCave(api_key=..., base_url=\"https://user:secret@gateway.example.com\", agent=...)\n# after\nCave(api_key=\"secret\", base_url=\"https://gateway.example.com\", agent=...)","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\n\ndef normalize_base_url(value: str) -> str:\n    p = urlsplit(value)\n    if p.scheme not in (\"http\", \"https\") or not p.hostname or p.username is not None:\n        raise ValueError(f\"base_url must be an absolute http(s) URL without credentials: {value!r}\")\n    return value.rstrip(\"/\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include the https:// scheme in configured hostnames.","Pass credentials through Cave(api_key=...), never inline in the URL.","Centralize URL normalization in one config-loading helper so every entrypoint gets the same check."],"tags":["python","sdk","url","validation","credentials"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}