{"record":{"id":"e8bfc1183075b986","repo":"langchain-ai/deepagents","slug":"field-references-unset-env-var-name-set-name","errorCode":null,"errorMessage":"{field} references unset env var {name}. Set {name} in the environment or provide a default.","messagePattern":"(.+?) references unset env var (.+?)\\. Set (.+?) in the environment or provide a default\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/mcp_config.py","lineNumber":71,"sourceCode":"        name = match.group(1)\n        default = match.group(2)\n        resolved = os.environ.get(name)\n        # A non-empty value always wins, for both `${VAR}` and `${VAR:-default}`.\n        if resolved:\n            return resolved\n        # `resolved` is now \"\" (set but empty) or None (unset).\n        if default is not None:\n            # `${VAR:-default}`: `:-` falls back for empty *and* unset (POSIX).\n            return default\n        if resolved is not None:\n            # `${VAR}` set to \"\": no default, so emit the empty value.\n            return resolved\n        # `${VAR}` unset with no default: the only hard error.\n        msg = (\n            f\"{field} references unset env var {name}. \"\n            f\"Set {name} in the environment or provide a default.\"\n        )\n        raise RuntimeError(msg)\n\n    # Reject any `${` that isn't the start of a well-formed reference. The\n    # check is against the raw `value` (not the substituted result) so a\n    # resolved value that happens to contain `${` never trips it.\n    ref_spans = [match.span() for match in _ENV_REF_RE.finditer(value)]\n    for brace in _ENV_BRACE_RE.finditer(value):\n        if not any(start <= brace.start() < end for start, end in ref_spans):\n            msg = (\n                f\"{field} contains a malformed '${{...}}' reference. \"\n                \"Use '${VAR}' or '${VAR:-default}'.\"\n            )\n            raise RuntimeError(msg)\n\n    return _ENV_REF_RE.sub(replace, value)\n\n\ndef _resolve_string(value: object, *, field: str) -> str:\n    \"\"\"Validate and interpolate one string field.","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/mcp_config.py#L53-L89","documentation":"`_interpolate_env` expands `${VAR}` references in MCP config string fields. A `${VAR}` reference (no `:-default`) whose variable is unset in `os.environ` is the only hard error: it raises `RuntimeError` naming the field and variable, so a typo or missing secret cannot silently inject an empty value into a URL, command, or header.","triggerScenarios":"Calling `resolve_mcp_server_env(server_name, server_config)` (directly or via login/connect) where a supported field value contains `${NAME}` and `NAME` is not present in `os.environ` and no `${NAME:-default}` form is used.","commonSituations":"A CI/container where the API-key or base-URL env var was never exported; shell config changed so a variable is no longer set; shared config copied between machines that define different variables; macOS launchers that don't inherit shell env.","solutions":["Set the named env var in the environment before launching (`export NAME=value`).","Change the config to `${NAME:-default}` so an unset variable falls back to a default.","Remove or rename the reference if the variable is obsolete — note only braced `${VAR}` expands; a bare `$VAR` passes through."],"exampleFix":"// before\n{\"url\": \"${ACME_API_URL}\"}   # ACME_API_URL unset -> RuntimeError\n// after\n{\"url\": \"${ACME_API_URL:-https://api.acme.dev}\"}","handlingStrategy":"validation","validationCode":"import os, re\nREF = re.compile(r\"\\$\\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^{}]*))?\\}\")\ndef missing_vars(value: str) -> list[str]:\n    refs = REF.findall(value)\n    return [name for name, default in refs if not default and not os.environ.get(name)]\n# e.g. missing_vars(cfg[\"url\"]) -> [\"MY_API_URL\"] before calling the API","typeGuard":"def env_available(name: str) -> bool:\n    return bool(os.environ.get(name))","tryCatchPattern":"try:\n    resolved = resolve_mcp_server_env(server_name, server_config)\nexcept RuntimeError as exc:\n    if \"references unset env var\" in str(exc):\n        var = str(exc).split(\"env var \")[1].split(\".\")[0]\n        raise SystemExit(f\"Set {var} before launching (export {var}=...)\") from exc\n    raise","preventionTips":["Prefer `${VAR:-default}` for anything optional","Check `env | grep VARNAME` before running the command if the config interpolates it","Remember containers/CI and GUI launchers do not inherit your shell env — set vars explicitly","Only `${VAR}` expands; a bare `$VAR` passes through unexpanded, so don't rely on it"],"tags":["mcp","env-var","config-validation"],"backgroundTag":"missing-env-var","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}