{"record":{"id":"ac88ad3c095c9ce9","repo":"langgenius/dify","slug":"plugin-remote-install-port-must-be-a-bare-port-num","errorCode":null,"errorMessage":"PLUGIN_REMOTE_INSTALL_PORT must be a bare port number, got {v}. A 'host:port' value usually means EXPOSE_PLUGIN_DEBUGGING_PORT was set to a compose publish spec like '127.0.0.1:5003'; bind loopback via a docker-compose.override.yaml instead of overloading this var.","messagePattern":"PLUGIN_REMOTE_INSTALL_PORT must be a bare port number, got (.+?)\\. A 'host:port' value usually means EXPOSE_PLUGIN_DEBUGGING_PORT was set to a compose publish spec like '127\\.0\\.0\\.1:5003'; bind loopback via a docker-compose\\.override\\.yaml instead of overloading this var\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"api/configs/feature/__init__.py","lineNumber":302,"sourceCode":"\n    NEW_USER_DEFAULT_PLUGIN_IDS: str = Field(\n        description=\"Comma-separated marketplace plugin IDs whose latest versions are installed for new users\",\n        default=\"\",\n    )\n\n    @field_validator(\"PLUGIN_REMOTE_INSTALL_PORT\", mode=\"before\")\n    @classmethod\n    def _reject_host_port_shaped_plugin_remote_install_port(cls, v):\n        \"\"\"Reject ``host:port``-shaped values with an actionable hint.\n\n        ``EXPOSE_PLUGIN_DEBUGGING_PORT`` is overloaded: it feeds both the\n        plugin_daemon ``ports:`` mapping (where ``127.0.0.1:5003`` is valid\n        compose syntax) and this integer app setting advertised in the console.\n        Without this guard a loopback bind spec crashloops the api container\n        with an opaque ``int_parsing`` traceback. See issue #39323.\n        \"\"\"\n        if isinstance(v, str) and \":\" in v.strip():\n            raise ValueError(\n                \"PLUGIN_REMOTE_INSTALL_PORT must be a bare port number, got \"\n                f\"{v!r}. A 'host:port' value usually means \"\n                \"EXPOSE_PLUGIN_DEBUGGING_PORT was set to a compose publish spec \"\n                \"like '127.0.0.1:5003'; bind loopback via a \"\n                \"docker-compose.override.yaml instead of overloading this var.\"\n            )\n        return v\n\n    @property\n    def NEW_USER_DEFAULT_PLUGIN_ID_LIST(self) -> list[str]:\n        return [item.strip() for item in self.NEW_USER_DEFAULT_PLUGIN_IDS.split(\",\") if item.strip()]\n\n    NEW_USER_DEFAULT_MODELS: str = Field(\n        description=(\"Comma-separated default models for new users in 'model_type:provider:model' format\"),\n        default=\"\",\n    )\n\n    @property","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/configs/feature/__init__.py#L284-L320","documentation":"Raised by the before-field-validator _reject_host_port_shaped_plugin_remote_install_port when PLUGIN_REMOTE_INSTALL_PORT is a string containing ':'. The setting must be a bare integer port; the EXPOSE_PLUGIN_DEBUGGING_PORT compose var is overloaded and feeding a publish spec like '127.0.0.1:5003' here would otherwise crash Pydantic int parsing and crashloop the api container (issue #39323).","triggerScenarios":"Setting PLUGIN_REMOTE_INSTALL_PORT (or EXPOSE_PLUGIN_DEBUGGING_PORT if it is wired to the same value) to '127.0.0.1:5003' or any 'host:port' string.","commonSituations":"Operator copies the compose 'ports:' publish value into the app-level env var, or aliases EXPOSE_PLUGIN_DEBUGGING_PORT to a bind spec.","solutions":["Set PLUGIN_REMOTE_INSTALL_PORT to a bare integer, e.g. '5003'.","Keep the loopback bind in docker-compose 'ports:' (or an override file) instead of the env var.","Decouple EXPOSE_PLUGIN_DEBUGGING_PORT so it only feeds the compose mapping, not the app setting."],"exampleFix":"// before\nPLUGIN_REMOTE_INSTALL_PORT=127.0.0.1:5003\n// after\nPLUGIN_REMOTE_INSTALL_PORT=5003","handlingStrategy":"validation","validationCode":"def is_bare_port(v) -> bool:\n    if isinstance(v, int):\n        return 0 <= v <= 65535\n    if isinstance(v, str):\n        s = v.strip()\n        return ':' not in s and s.isdigit() and 0 <= int(s) <= 65535\n    return False","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep EXPOSE_PLUGIN_DEBUGGING_PORT (compose) and PLUGIN_REMOTE_INSTALL_PORT (app) as separate values.","Document the loopback bind in a docker-compose.override.yaml.","Add a CI lint that rejects ':' in PLUGIN_REMOTE_INSTALL_PORT."],"tags":["config","pydantic","docker-compose","plugin"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}