opendataloader-project/opendataloader-pdf · error · ConfigError
{f}: {e}
Error message
{f}: {e} What it means
ConfigError (exit code 2) raised by _read when read_text(encoding='utf-8') on a scanned prose file raises OSError — permission denied, I/O error, or an unreadable file. The message echoes the file path and the underlying OSError. Like error 96 this is an input/config problem (exit 2), reported distinctly from a coupling violation (exit 1), so CI can tell a broken environment from a real lint failure.
Source
Thrown at skills/odl-pdf-maintenance/sync-skill-refs.py:100
distinct from exit 1 (a coupling/structural violation in the authored prose)."""
def _scanned_files(skill_dir: Path):
"""Agent-facing prose: SKILL.md + references/*.md. ConfigError if the dir does
not look like a skill bundle (no SKILL.md) — an empty/wrong --skill-dir must not
silently scan 0 files and report a false PASS."""
skill_md = skill_dir / "SKILL.md"
if not skill_md.is_file():
raise ConfigError(f"no SKILL.md found under {skill_dir} (nothing to lint)")
files = [skill_md] + sorted((skill_dir / "references").glob("*.md"))
return [f for f in files if f.is_file()]
def _read(f: Path) -> str:
try:
return f.read_text(encoding="utf-8")
except OSError as e:
raise ConfigError(f"{f}: {e}") from e
def _is_ipv4(run: str) -> bool:
"""A dotted run that is a valid dotted-quad IPv4 (4 octets, each 0-255)."""
parts = run.split(".")
return len(parts) == 4 and all(p.isdigit() and 0 <= int(p) <= 255 for p in parts)
def check_versions(rel: str, text: str, violations: list):
"""Check 1 — baked semver, excluding dotted-quad IPv4."""
for i, line in enumerate(text.splitlines(), 1):
for m in _DOTTED_RE.finditer(line):
run = m.group(0)
if _is_ipv4(run):
continue # IPv4 safety advice, not a version
if run.count(".") >= 2: # 3+ components -> semver-like
violations.append(
f"{rel}:{i} baked version '{run}' "View on GitHub (pinned to a7789b8e77)
Solutions
- Fix permissions: chmod/chown the file so the lint process can read it (e.g. chmod 644).
- Replace broken symlinks with real files or valid symlinks.
- Re-run; if the OSError is a transient mount/IO error, remount or retry after the FS recovers.
- Confirm the file is not a special file/device that read_text cannot handle.
Example fix
# before: unreadable file $ ls -l references/eval-metrics.md -rw------- 1 root root ... references/eval-metrics.md # CI user cannot read -> ConfigError # after: world-readable $ chmod 644 references/eval-metrics.md $ ./sync-skill-refs.py --skill-dir skills/odl-pdf
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def all_prose_readable(skill_dir: str) -> bool:
files = [Path(skill_dir) / "SKILL.md"] + list((Path(skill_dir) / "references").glob("*.md"))
return all(f.is_file() and os.access(f, os.R_OK) for f in files if f.is_file())
Type guard
def is_unreadable_file_error(exc: Exception) -> bool:
return exc.__class__.__name__ == "ConfigError" and ": " in str(exc) and "SKILL.md found" not in str(exc) Try / catch
import subprocess, sys
rc = subprocess.run([sys.executable, "sync-skill-refs.py", "--skill-dir", d]).returncode
if rc == 2:
# could be error 96 or 97; re-run verbosely to see which file
print("Config error (exit 2): check --skill-dir exists and all prose files are readable (chmod 644)") Prevention
- Ensure all scanned prose files are readable by the lint user (chmod 644).
- Replace broken symlinks in references/ with real files.
- Distinguish exit 2 (config/IO) from exit 1 (real violation) in CI scripting.
When it happens
Trigger: The lint scans SKILL.md or a references/*.md file that exists but cannot be read — permission denied (mode 000), an I/O error on a failing disk/NFS mount, or a broken symlink that lstat'd as a file but cannot be opened.
Common situations: File permissions prevent the CI user from reading the file (chmod 600 owned by another user). A broken symlink inside references/. An NFS mount hiccup. A file locked/exclusive by another process.
Related errors
- no SKILL.md found under {skill_dir} (nothing to lint)
- OCR engine '{ocr_engine}' is not supported in hybrid local m
- Unknown ocr_engine '{ocr_engine}': {e}\nAvailable engines: {
- Cannot use the temporary directory '{tempDir}'. PDF processi
- Options --hybrid-hancom-ai-* require --hybrid=hancom-ai (got
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/5ffe4702940ddc65.
Report an issue: GitHub.