{"record":{"id":"438371f2496677de","repo":"iflytek/astron-agent","slug":"tool-url-has-unresolved-path-parameters","errorCode":null,"errorMessage":"Tool URL has unresolved path parameters","messagePattern":"Tool URL has unresolved path parameters","errorType":"exception","errorClass":"OutboundPolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/link/infra/tool_exector/process.py","lineNumber":135,"sourceCode":"        # persisted endpoint path.\n        for name, value in self.path.items():\n            placeholder = \"{\" + str(name) + \"}\"\n            if placeholder not in url:\n                raise OutboundPolicyError(\n                    f\"Tool path parameter has no matching placeholder: {name}\"\n                )\n            raw_value = str(value)\n            if (\n                raw_value in {\".\", \"..\"}\n                or \"/\" in raw_value\n                or \"\\\\\" in raw_value\n                or any(ord(character) < 0x20 for character in raw_value)\n            ):\n                raise OutboundPolicyError(\"Tool path parameter is unsafe\")\n            url = url.replace(placeholder, quote(raw_value, safe=\"\"))\n\n        if _PATH_PARAMETER_PATTERN.search(url):\n            raise OutboundPolicyError(\"Tool URL has unresolved path parameters\")\n\n        # Authentication method selection and URL construction\n        if self._is_authorization_md5:\n            url = public_query_url(url)\n            if self.query:\n                url = url + \"&\" + \"&\".join([f\"{k}={v}\" for k, v in self.query.items()])\n        elif self._is_auth_hmac:\n            url, headers = assemble_ws_auth_url(\n                url, self.method, self.auth_con_js, self.body\n            )\n            self.header = headers\n        else:\n            if self.query:\n                url = url + \"?\" + \"&\".join([f\"{k}={v}\" for k, v in self.query.items()])\n\n        # Authentication helpers may add query data, but must not replace the endpoint origin.\n        ensure_same_origin(self.server, url)\n        return url","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/link/infra/tool_exector/process.py#L117-L153","documentation":"After substituting all supplied path parameters, the library scans the final URL for any remaining {placeholder} tokens. If any are left, it means the endpoint template requires parameters that were not supplied, so the request would hit a literal '{...}' path. It raises OutboundPolicyError('Tool URL has unresolved path parameters') to fail closed instead of making a doomed request.","triggerScenarios":"do_call → _build_url where the server URL template contains {placeholders} (e.g. /users/{userId}/posts/{postId}) but self.path does not include all of those keys — a required path parameter was omitted from the tool call arguments.","commonSituations":"Caller omitted an optional-looking but template-required path variable; schema added a new placeholder after the client was written; LLM-generated tool arguments dropped a parameter.","solutions":["Supply a value for every {placeholder} named in the tool's endpoint template in the path parameter map","Regenerate/refresh the tool schema so the client knows all required path parameters","Add client-side validation that all template placeholders have corresponding arguments before calling"],"exampleFix":"// before\npath = {\"userId\": \"7\"}  # template: /users/{userId}/posts/{postId}\n// after\npath = {\"userId\": \"7\", \"postId\": \"33\"}","handlingStrategy":"validation","validationCode":"import re\ndef all_placeholders_filled(template, params):\n    return not re.search(r\"\\{([^{}]+)\\}\", template.replace(\"{\", \"{\").format(**{}) if False else _substituted(template, params))\ndef _substituted(t, p):\n    for k, v in p.items():\n        t = t.replace(\"{\" + str(k) + \"}\", str(v))\n    return t","typeGuard":null,"tryCatchPattern":"try:\n    result = await run.do_call(span)\nexcept CallThirdApiException as e:\n    if 'unresolved path parameters' in str(e.err):\n        missing = re.findall(r\"\\{([^{}]+)\\}\", expected_url)\n        raise MissingToolArgs(missing) from e\n    raise","preventionTips":["Enumerate all template placeholders when generating tool call arguments","Refresh client schemas when endpoint templates change","Validate arguments against the tool's OpenAPI parameters before dispatch"],"tags":["python","openapi","url-template"],"backgroundTag":"missing-required-argument","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"}