{"record":{"id":"4fc82f640a01f1be","repo":"iflytek/astron-agent","slug":"sandbox-runtime-configuration-is-unavailable","errorCode":null,"errorMessage":"Sandbox runtime configuration is unavailable","messagePattern":"Sandbox runtime configuration is unavailable","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"core/agent/service/plugin/skill_sandbox.py","lineNumber":216,"sourceCode":"    runtime_config_url = (os.getenv(RUNTIME_CONFIG_URL_ENV) or \"\").strip()\n    try:\n        parsed = urlsplit(runtime_config_url)\n        if (\n            not runtime_config_url\n            or len(runtime_config_url) > 2048\n            or any(char in runtime_config_url for char in (\"\\r\", \"\\n\", \"\\t\"))\n            or 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            or parsed.path != RUNTIME_CONFIG_PATH\n            or bool(parsed.query)\n            or bool(parsed.fragment)\n        ):\n            raise ValueError\n        parsed.port\n    except (TypeError, ValueError):\n        raise RuntimeError(SANDBOX_RUNTIME_CONFIG_ERROR) from None\n    return runtime_config_url\n\n\ndef _load_runtime_credential_token() -> str:\n    token = (os.getenv(RUNTIME_CREDENTIAL_TOKEN_ENV) or \"\").strip()\n    if not token:\n        token_file = (os.getenv(RUNTIME_CREDENTIAL_TOKEN_FILE_ENV) or \"\").strip()\n        if token_file:\n            try:\n                token = Path(token_file).read_text(encoding=\"utf-8\").strip()\n            except (OSError, UnicodeError):\n                raise RuntimeError(SANDBOX_RUNTIME_CONFIG_ERROR) from None\n    if (\n        len(token) < MIN_RUNTIME_CREDENTIAL_TOKEN_LENGTH\n        or \"\\r\" in token\n        or \"\\n\" in token\n    ):\n        raise RuntimeError(SANDBOX_RUNTIME_CONFIG_ERROR)","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/agent/service/plugin/skill_sandbox.py#L198-L234","documentation":"_load_runtime_config_url validates SKILL_SANDBOX_RUNTIME_CONFIG_URL with the same strict rules as the artifact upload URL but for the runtime-config path: scheme http/https, hostname present, no userinfo, path exactly '/skill-sandbox/internal-runtime-config', no query/fragment, <= 2048 chars, no control chars, valid port. Violations become RuntimeError('Sandbox runtime configuration is unavailable'), raised when _fetch_e2b_runtime_config runs.","triggerScenarios":"_fetch_e2b_runtime_config invoked (sandbox script execution path) while SKILL_SANDBOX_RUNTIME_CONFIG_URL is unset/empty, points to the wrong path (not /skill-sandbox/internal-runtime-config), uses a non-http(s) scheme, contains credentials/query/fragment/control characters, exceeds 2048 chars, or has an invalid port — converted at line 216.","commonSituations":"E2B sandbox configured with a token/URL pair for a different endpoint version; operator set the root URL without the internal-runtime-config path; the runtime-config service was moved behind a gateway that appends a path prefix; empty env var after a broken docker-compose variable substitution (`${VAR}` left blank).","solutions":["Set SKILL_SANDBOX_RUNTIME_CONFIG_URL to the exact URL ending in /skill-sandbox/internal-runtime-config, e.g. https://sandbox.example.com/skill-sandbox/internal-runtime-config","Remove query strings, fragments, credentials, or control characters and keep total length <= 2048","Check docker-compose/helm values so the variable is actually substituted (not left as an empty string) at deploy time","Confirm the path against the deployed sandbox runtime-config service version"],"exampleFix":"// before\nSKILL_SANDBOX_RUNTIME_CONFIG_URL=https://sandbox.example.com\n\n// after\nSKILL_SANDBOX_RUNTIME_CONFIG_URL=https://sandbox.example.com/skill-sandbox/internal-runtime-config","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\n\nRUNTIME_CONFIG_PATH = \"/skill-sandbox/internal-runtime-config\"\n\ndef runtime_config_url_ok() -> bool:\n    url = (os.getenv(\"SKILL_SANDBOX_RUNTIME_CONFIG_URL\") or \"\").strip()\n    if not url or len(url) > 2048 or any(c in url for c in \"\\r\\n\\t\"):\n        return False\n    p = urlsplit(url)\n    try:\n        p.port\n    except ValueError:\n        return False\n    return (\n        p.scheme in (\"http\", \"https\")\n        and bool(p.hostname)\n        and p.username is None\n        and p.password is None\n        and p.path == RUNTIME_CONFIG_PATH\n        and not p.query\n        and not p.fragment\n    )","typeGuard":"from urllib.parse import urlsplit\n\ndef is_valid_runtime_config_url(url: object) -> bool:\n    if not isinstance(url, str) or not url:\n        return False\n    p = urlsplit(url)\n    return p.scheme in (\"http\", \"https\") and bool(p.hostname) and p.path == \"/skill-sandbox/internal-runtime-config\"","tryCatchPattern":"try:\n    config = await _fetch_e2b_runtime_config()\nexcept RuntimeError as exc:\n    if \"Sandbox runtime configuration\" in str(exc):\n        raise ConfigError(\n            \"SKILL_SANDBOX_RUNTIME_CONFIG_URL missing/malformed; must end in \"\n            \"/skill-sandbox/internal-runtime-config\"\n        ) from exc\n    raise","preventionTips":["Set the full path URL, not the service root, in compose/helm values","Check rendered templates (`docker compose config`) so unresolved ${VAR} substitutions are caught before deploy","Run the same URL allowlist validation in a pre-deploy smoke test","Keep the runtime-config endpoint path in sync with the sandbox service version you deploy"],"tags":["configuration","env-var","url-validation","sandbox"],"backgroundTag":"invalid-url","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}