infiniflow/ragflow · error · SandboxProviderConfigError

Failed to load configured SSH known_hosts file.

Error message

Failed to load configured SSH known_hosts file.

What it means

Raised as SandboxProviderConfigError when the operator configured a known_hosts file (self.known_hosts) but client.load_host_keys() raised OSError — file missing, unreadable, or a directory. The provider deliberately fails closed: continuing with only system keys could let the connection trust an unintended anchor (e.g. an attacker-writable ~/.ssh/known_hosts), matching the Go provider's posture. The OSError is chained and a warning is logged.

Source

Thrown at agent/sandbox/providers/ssh.py:464

        # RejectPolicy would reject every host on first connect,
        # breaking the provider for normal setups. The order matters:
        # load_system_host_keys() populates the store from
        # ~/.ssh/known_hosts (and the legacy /etc/ssh/ssh_known_hosts);
        # an optional explicit known_hosts file from `known_hosts`
        # config is then merged on top.
        client.load_system_host_keys()
        if self.known_hosts:
            try:
                client.load_host_keys(self.known_hosts)
            except OSError as exc:
                # Fail closed when the operator-configured trust store
                # is unreadable: continuing with system keys could let
                # the connection succeed against an unintended anchor
                # (e.g. an attacker who can write ~/.ssh/known_hosts).
                # Match the Go provider's fail-closed posture (see
                # internal/agent/sandbox/ssh.go::hostKeyCallback).
                logging.warning("SSH: failed to load configured known_hosts file; refusing connection")
                raise SandboxProviderConfigError("Failed to load configured SSH known_hosts file.") from exc
        # Reject unknown hosts: this is the default fail-closed posture
        # to prevent silent MITM. Operators must either ship a populated
        # known_hosts file or accept the warning (paramiko will fail the
        # connect) on first encounter.
        client.set_missing_host_key_policy(paramiko.RejectPolicy())

        connect_kwargs: dict[str, Any] = {
            "hostname": self.host,
            "port": self.port,
            "username": self.username,
            "timeout": self.timeout,
            "banner_timeout": self.timeout,
            "auth_timeout": self.timeout,
            "look_for_keys": False,
            "allow_agent": False,
        }
        if self.private_key:
            connect_kwargs["pkey"] = self._load_private_key()

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Verify the file exists and is readable by the service user: ls -l <known_hosts path>
  2. Create it if missing: ssh-keyscan -H <host> > <known_hosts> (run from a trusted network)
  3. Fix permissions/ownership: chown <service-user> <file> && chmod 644 <file>
  4. Or clear the known_hosts config option to rely on system host keys (still fail-closed via RejectPolicy)

Example fix

# before
provider.initialize({..., "known_hosts": "/etc/ragflow/known_hosts"})  # file absent -> fail closed

# after
ssh-keyscan -H 10.0.0.5 > /etc/ragflow/known_hosts && chmod 644 /etc/ragflow/known_hosts
provider.initialize({..., "known_hosts": "/etc/ragflow/known_hosts"})
Defensive patterns

Strategy: validation

Validate before calling

import os
kh = config.get("known_hosts", "")
if kh and not (os.path.isfile(kh) and os.access(kh, os.R_OK)):
    raise RuntimeError(f"known_hosts path {kh!r} missing or unreadable; run ssh-keyscan first")

Try / catch

try:
    provider.initialize(config)
except SandboxProviderConfigError as e:
    if "known_hosts" in str(e):
        subprocess.run(f"ssh-keyscan -H {config['host']} > {config['known_hosts']}", shell=True, check=True)
        provider.initialize(config)

Prevention

When it happens

Trigger: initialize() with known_hosts pointing to a path that does not exist; file present but wrong ownership/permissions (OSError EACCES) especially when the app runs as a different user; path is a directory; container image built without copying the known_hosts file.

Common situations: Deploying with a config template referencing /etc/ssh/ssh_known_hosts that is absent in a slim container; file mounted read-only with restrictive mode; path typo or trailing whitespace in the config value (it is stripped, but case-sensitivity on paths still bites).

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/dcbe39e10b8215a0. Report an issue: GitHub.