Panniantong/Agent-Reach · error · McporterConfigError
mcporter 配置文件不存在
Error message
mcporter 配置文件不存在
What it means
Raised when read_small_text_no_follow() returns None for a config path — its contract for FileNotFoundError — yet the layer-selection step (os.path.lexists) saw the path exist. So the file vanished between selection and open, or a broken symlink existed (lexists true, open ENOENT, since O_NOFOLLOW refuses the link itself).
Source
Thrown at agent_reach/channels/mcporter.py:123
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``.
Paths, descriptions, endpoints, and other metadata are deliberately
ignored: only each server object's ``name`` field is a routing signal.
"""
try:
payload = json.loads(output)
except (json.JSONDecodeError, TypeError) as exc:View on GitHub (pinned to 93ae1d18c3)
Solutions
- If a broken symlink: remove it or repoint it at a real target — ls -la the path to confirm
- Re-run doctor; if it was a transient atomic-save race it resolves once the writer finishes
- Avoid running doctor concurrently with config-regeneration tooling
Example fix
# before: ~/.config/mcporter/mcporter.json -> /mnt/removed/share/mcporter.json (dangling) # after rm ~/.config/mcporter/mcporter.json # layer then skipped, or restore a real file
Defensive patterns
Strategy: retry
Validate before calling
import os
def layer_stable(path) -> bool:
return os.path.exists(path) and not os.path.islink(path) or (os.path.islink(path) and os.path.exists(os.path.realpath(path))) Try / catch
from agent_reach.channels.mcporter import McporterConfigError
for attempt in range(2):
try:
names = inspect_mcporter_config(root).names
break
except McporterConfigError as exc:
if "不存在" in str(exc) and attempt == 0:
continue # transient atomic-save race; retry once
raise Prevention
- Don't run doctor while another tool rewrites mcporter configs
- Remove dangling symlinks immediately after dotfile cleanup
- Atomic-save writers should replace files, not unlink+create
When it happens
Trigger: Race: file deleted/moved between _select_config_layers() and the read (concurrent git checkout, editor atomic save, another doctor run). Or the path is a dangling symlink: lexists() returns True, os.open with O_NOFOLLOW hits ENOENT → returns None → this error.
Common situations: Broken symlink after dotfile repo cleanup; editors (and agents) doing rename-based atomic writes while doctor runs; config managed by a watcher that recreates files.
Related errors
- mcporter 配置文件无法安全读取:{exc}
- mcporter 配置文件无法安全读取
- mcporter 配置缺少 mcpServers 对象
- mcporter 配置包含无效的 server name
- mcporter server 定义必须是对象
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/04a1401c680648b1.
Report an issue: GitHub.