langgenius/dify · error · ValueError

PLUGIN_REMOTE_INSTALL_PORT must be a bare port number, got {

Error message

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.

What it means

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).

Source

Thrown at api/configs/feature/__init__.py:302

    NEW_USER_DEFAULT_PLUGIN_IDS: str = Field(
        description="Comma-separated marketplace plugin IDs whose latest versions are installed for new users",
        default="",
    )

    @field_validator("PLUGIN_REMOTE_INSTALL_PORT", mode="before")
    @classmethod
    def _reject_host_port_shaped_plugin_remote_install_port(cls, v):
        """Reject ``host:port``-shaped values with an actionable hint.

        ``EXPOSE_PLUGIN_DEBUGGING_PORT`` is overloaded: it feeds both the
        plugin_daemon ``ports:`` mapping (where ``127.0.0.1:5003`` is valid
        compose syntax) and this integer app setting advertised in the console.
        Without this guard a loopback bind spec crashloops the api container
        with an opaque ``int_parsing`` traceback. See issue #39323.
        """
        if isinstance(v, str) and ":" in v.strip():
            raise ValueError(
                "PLUGIN_REMOTE_INSTALL_PORT must be a bare port number, got "
                f"{v!r}. 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."
            )
        return v

    @property
    def NEW_USER_DEFAULT_PLUGIN_ID_LIST(self) -> list[str]:
        return [item.strip() for item in self.NEW_USER_DEFAULT_PLUGIN_IDS.split(",") if item.strip()]

    NEW_USER_DEFAULT_MODELS: str = Field(
        description=("Comma-separated default models for new users in 'model_type:provider:model' format"),
        default="",
    )

    @property

View on GitHub (pinned to ef8544b173)

Solutions

  1. Set PLUGIN_REMOTE_INSTALL_PORT to a bare integer, e.g. '5003'.
  2. Keep the loopback bind in docker-compose 'ports:' (or an override file) instead of the env var.
  3. Decouple EXPOSE_PLUGIN_DEBUGGING_PORT so it only feeds the compose mapping, not the app setting.

Example fix

// before
PLUGIN_REMOTE_INSTALL_PORT=127.0.0.1:5003
// after
PLUGIN_REMOTE_INSTALL_PORT=5003
Defensive patterns

Strategy: validation

Validate before calling

def is_bare_port(v) -> bool:
    if isinstance(v, int):
        return 0 <= v <= 65535
    if isinstance(v, str):
        s = v.strip()
        return ':' not in s and s.isdigit() and 0 <= int(s) <= 65535
    return False

Prevention

When it happens

Trigger: 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.

Common situations: Operator copies the compose 'ports:' publish value into the app-level env var, or aliases EXPOSE_PLUGIN_DEBUGGING_PORT to a bind spec.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/ac88ad3c095c9ce9. Report an issue: GitHub.