Panniantong/Agent-Reach · error · McporterConfigError
mcporter 配置文件无法安全读取:{exc}
Error message
mcporter 配置文件无法安全读取:{exc} What it means
Raised when reading a mcporter config layer fails the security preconditions in read_small_text_no_follow(): a symlink anywhere in the path, the target not being a regular file, or the file exceeding _MAX_CONFIG_BYTES. The message embeds the underlying PrivatePathError detail. This is a deliberate anti-symlink-attack guard for files that influence tool routing.
Source
Thrown at agent_reach/channels/mcporter.py:117
candidate = home_base / name
if os.path.lexists(candidate):
layers.append((candidate, "home"))
break
project_path = root / "config" / "mcporter.json"
if os.path.lexists(project_path):
layers.append((project_path, "project"))
return layers
def _read_config_object(config_path: Path) -> dict:
try:
raw = read_small_text_no_follow(
config_path,
max_bytes=_MAX_CONFIG_BYTES,
)
except PrivatePathError as exc:
raise McporterConfigError(
f"mcporter 配置文件无法安全读取:{exc}"
) from exc
except (OSError, UnicodeError) as exc:
raise McporterConfigError("mcporter 配置文件无法安全读取") from exc
if raw is None:
raise McporterConfigError("mcporter 配置文件不存在")
try:
payload = json.loads(raw)
except json.JSONDecodeError as exc:
raise McporterConfigError("mcporter 配置不是有效的 UTF-8 JSON") from exc
if not isinstance(payload, dict):
raise McporterConfigError("mcporter 配置顶层必须是对象")
return payload
def configured_server_names(output: str) -> set[str]:
"""Return exact configured server names from ``mcporter ... --json``.
View on GitHub (pinned to 93ae1d18c3)
Solutions
- Resolve symlinks: replace links with real files (cp -L) so every component of the path is a real directory/file
- Verify with: python -c "import os;p='<file>';print(os.path.realpath(p))" and compare, and ls -la each parent
- Shrink the config below the size cap — split extra servers into imported files
- Exclude the path from symlink-based dotfile management
Example fix
# before: ~/.config/mcporter/mcporter.json -> ~/dotfiles/mcporter.json # after cp -L ~/dotfiles/mcporter.json ~/.config/mcporter/mcporter.json
Defensive patterns
Strategy: validation
Validate before calling
import os
def path_is_symlink_free(path) -> bool:
p = os.path.abspath(path)
while True:
if os.path.islink(p):
return False
parent = os.path.dirname(p)
if parent == p:
return True
p = parent Try / catch
except McporterConfigError as exc:
if "无法安全读取" in str(exc) and "symlink" in str(exc.__cause__ or ""):
advise("materialize the config as a real file") Prevention
- Keep mcporter configs as real files on real directories
- Exclude config paths from symlink dotfile managers
- Split oversized configs; respect the size cap as a hard invariant
When it happens
Trigger: mcporter.json (or its parent dir) is a symlink; the file is a FIFO/device; the file is larger than _MAX_CONFIG_BYTES. Distinct from error 11: this branch carries the PrivatePathError, so the cause is a policy refusal, not a plain I/O failure.
Common situations: Dotfiles managers symlinking ~/.config/mcporter; config kept in a mounted/virtual path resolving through links; a pathological huge config (bundled server definitions pasted repeatedly).
Related errors
- gh hosts.yml 无法安全读取
- mcporter 配置缺少 mcpServers 对象
- mcporter 配置包含无效的 server name
- mcporter server 定义必须是对象
- mcporter imports 必须是字符串列表
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/5a17c902cea69c11.
Report an issue: GitHub.