Graphify-Labs/graphify · error · AttributeError
module {__name__!r} has no attribute {name!r}
Error message
module {__name__!r} has no attribute {name!r} What it means
A deliberate shrink-guard (#479) in the graphify export step (tools/skillgen/expected/graphify__skill-vscode.md:433). graphify.export.to_json(G, communities, 'graphify-out/graph.json') compares the new graph's node count against the existing graph.json and returns False (writing nothing) when the new graph is smaller. The script then exits 1 so that GRAPH_REPORT.md and .graphify_analysis.json are never written for a graph that graph.json does not actually contain (#1392).
Source
Thrown at graphify/__main__.py:155
_ALWAYS_ON_ALIASES = {
"_CLAUDE_MD_SECTION": "claude-md",
"_AGENTS_MD_SECTION": "agents-md",
"_GEMINI_MD_SECTION": "gemini-md",
"_VSCODE_INSTRUCTIONS_SECTION": "vscode-instructions",
"_ANTIGRAVITY_RULES": "antigravity-rules",
"_KIRO_STEERING": "kiro-steering",
}
def __getattr__(name: str) -> str:
# PEP 562: lazily resolve the legacy always-on section constants for external
# importers (e.g. the install-string tests). In-module code calls _always_on()
# directly; nothing is read at import time, so a missing block can no longer
# brick the CLI on `import graphify.__main__` (#1121 follow-up).
base = _ALWAYS_ON_ALIASES.get(name)
if base is not None:
return _always_on(base)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def _check_skill_version(skill_dst: Path) -> None:
"""Warn if the installed skill is from an older graphify version."""
version_file = skill_dst.parent / ".graphify_version"
try:
if not version_file.exists():
return
except OSError:
return
try:
skill_exists = skill_dst.exists()View on GitHub (pinned to 7fe58b0b0f)
Solutions
- If the shrink is intentional (files were deleted from the corpus), re-run a full build with --force as the error message instructs, so graph.json is overwritten regardless of node count.
- If the shrink is NOT intentional, inspect why extraction produced fewer nodes: skipped files, changed skip rules, wrong INPUT_PATH, or a partial extraction.
- Compare node counts before deciding: read the 'nodes' length in the existing graphify-out/graph.json versus G.number_of_nodes() printed by the pipeline.
- If graphify-out is stale from another project, delete graphify-out/ entirely and rebuild from scratch.
Example fix
# before: incremental update refuses to shrink $(cat graphify-out/.graphify_python) graphify update . # ERROR: refused to shrink ... (#479) # after: intentional shrink -> force full rebuild $(cat graphify-out/.graphify_python) graphify build --force .
Defensive patterns
Strategy: validation
Validate before calling
import json
from pathlib import Path
existing = Path('graphify-out/graph.json')
if existing.exists():
old_n = len(json.loads(existing.read_text(encoding='utf-8')).get('nodes', []))
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding='utf-8'))
new_n = len(extract.get('nodes') or [])
if new_n < old_n:
print(f'planned shrink {old_n} -> {new_n}: pass --force if intentional, else investigate skipped files') Prevention
- Compare new vs existing node counts before export; pass --force only when the corpus shrink is intentional.
- After deleting files from the corpus, always follow with a full --force rebuild rather than a plain update.
- Inspect .graphify_detect.json for skipped files whenever the node count drops unexpectedly.
- Never reuse a graphify-out/ directory across different repositories or INPUT_PATH values.
When it happens
Trigger: Calling to_json() on a graph whose node count is lower than the node count stored in the already-existing graphify-out/graph.json. Typical trigger: an incremental --update re-extract that found fewer files (or skipped some) than the original full build, or the corpus genuinely shrank because files were deleted.
Common situations: Running graphify update after deleting source files; partial re-extraction where some files were skipped; stale graph.json from a previous larger corpus left in graphify-out/; switching INPUT_PATH to a smaller subtree without clearing the old output.
Related errors
- Cannot read graph file {path}: {exc}. Re-run 'graphify extra
- cannot stat {p}: {exc}
- ERROR: refused to shrink graphify-out/graph.json (existing g
- ERROR: refused to shrink graphify-out/graph.json (existing g
- ERROR: refused to shrink graphify-out/graph.json (fewer node
AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14).
Data as JSON: /api/errors/449516cead7f134d.
Report an issue: GitHub.