Troubleshooting
Task-oriented diagnostics for astwire, grounded in the current implementation's actual behavior (cli.py, discover.py, the per-language engines in src/lang/).
On this page ▾
- 1. astwire: No matching files found.
- Symptom
- Cause
- Checks
- Resolution
- 2. astwire: No matching files found (skipped N unregistered file(s)).
- Symptom
- Cause
- Resolution
- 3. -i Finds Fewer Files Than Expected
- Symptom
- Checks
- Resolution
- 4. Import Resolution and .gitignore / .astwireignore
- Symptom
- Cause
- Resolution
- 5. Project Root or Config Is Not What You Expect
- Symptom
- Cause
- Checks
- Resolution
- 6. Secret Not Redacted
- Symptom
- Cause
- Resolution
- 7. --analysis Output Looks Wrong / Token Counts Differ From Expectations
- Symptom
- Cause
- Checks
- Resolution
- 8. Nuitka Compilation Fails Locally
- Symptom
- Checks
- Resolution
- 9. Windows PATH Not Updated After Install
- Symptom
- Cause
- Resolution
- 10. astwire --uninstall Requires Elevated Permissions
- Symptom (macOS/Linux)
- Cause
- Resolution
- 11. macOS Blocks the Downloaded Binary (Gatekeeper)
- Symptom
- Cause
- Resolution
- 12. Config File Silently Ignored
- Symptom
- Cause
- Checks
- Resolution
- 13. astwire: --since ... Errors
- Symptom
- Cause
- Checks
- Resolution
- Common Recovery Matrix
- Escalation
1. astwire: No matching files found.
Symptom
astwire: No matching files found.Exit code 1.
Cause
resolve_named_targets() (the default, non--i path) returned zero files — every candidate was excluded by skip patterns, .gitignore/.astwireignore, or the given target simply doesn't exist / contains no files.
Checks
ls -la <target>
cat .astwire.toml 2>/dev/null
cat .astwireignore 2>/dev/null
git check-ignore -v <target>/<file>Resolution
Verify the target path is correct and not entirely excluded by .gitignore/.astwireignore/skip. If you intended to bundle a specific file that's gitignored, either remove it from the ignore file or target it explicitly (an explicitly-named single file is still checked against ignore rules — it is not exempt).
2. astwire: No matching files found (skipped N unregistered file(s)).
Symptom
astwire: No matching files found (skipped 3 unregistered files).Cause
Target resolution matched files, but none had an extension in astwire's language registry (astwire --languages prints the current list — Python, JS/TS/TSX, Go, and a range of include-only languages like JSON/YAML/Markdown/HTML/CSS/etc.). Unregistered files are counted but never read or bundled.
Resolution
Point astwire at directories that contain registered file types, verify the target's files use a recognized extension, or check astwire --languages if you expected a given extension to be supported.
3. -i Finds Fewer Files Than Expected
Symptom
astwire <entry> -i bundles fewer files than the entry point actually imports.
Checks
Import resolution (expand_local_imports() in discover.py, dispatching per-file to each language's local_imports() engine) only follows imports it can statically resolve to a local file under the detected project root, and only for languages with an import engine (Python, JS/TS/TSX, Go). It will not follow:
- Dynamic imports (Python's
importlib.import_module(name),__import__(name); JS's computedrequire(someVar)) — none of the engines evaluate expressions, only literal specifiers. - Python imports inside a
try/except ImportErrorblock that reference optional dependencies — the AST visitor still sees them, but if they resolve to a third-party package (not found under the project root), they're correctly skipped, not treated as an error. - Python files with a
SyntaxError—ast.parse()failures are silently skipped (PythonEngine.local_imports()catchesSyntaxError/ValueError/UnicodeDecodeErrorand returns no imports for that file), so a single broken file can silently prune an entire subtree of the import graph. JS/TS's regex-based scanner and Go's parser are more tolerant of malformed syntax but can still miss non-literal or unconventionally-formatted imports. - JS/TS bare specifiers (
react,#internal),node:/http:/https:/data:imports, anything undernode_modules/, ortsconfig.jsonpath aliases — only relative (./,../) specifiers are followed. - Go imports outside the current module (resolved via the nearest
go.mod), or anything underGOROOT/the module cache. - Imports resolved to a path outside
project_root— every engine's resolution is discarded if it lands outside the detected root. - A resolved import that
skip/.gitignore/.astwireignorewould exclude — as of SPEC-1.0.md §5, skip/ignore rules win over-i, so an import target matching any of them is silently dropped from the bundle. See Section 4 below.
Resolution
python3 -c "import ast; ast.parse(open('path/to/file.py').read())"Run this against Python files you expect to be included, to rule out a silent SyntaxError skip. Confirm the detected project root is what you expect — see Section 5. If the missing file matches a skip pattern or is gitignored, that's expected — see Section 4.
4. Import Resolution and .gitignore / .astwireignore
Symptom
You expected a file excluded by .gitignore/.astwireignore/skip to still show up in -i output because it's imported by something in the bundle — but it doesn't.
Cause
This is expected current behavior, not a bug: as of SPEC-1.0.md §5, skip/ignore rules win over -i. expand_local_imports() (discover.py) checks every resolved import target against the same IgnoreEngine/skip rules used for a plain target walk, in addition to hard-denying vendor-style dirnames (node_modules/, vendor/, .venv/, etc.) outright. A file matching skip, .gitignore, or .astwireignore is dropped from the import expansion even if something in the bundle imports it.
Resolution
If you need that file included, remove it from .gitignore/.astwireignore/.astwire.toml's skip list, or pass it as an explicit target rather than relying on -i to pull it in.
5. Project Root or Config Is Not What You Expect
Symptom
.astwire.toml settings don't seem to apply, or output paths are relative to an unexpected directory.
Cause
find_project_root() walks upward from the first target looking for the first of .git, .astwire.toml, pyproject.toml, setup.py, manage.py. If none exists before the filesystem root, it falls back to the target itself (or its parent, if the target is a file) — which can be a surprising root if your repository genuinely has none of those markers.
load_config() then searches upward from that detected root for .astwire.toml — so a config file that exists but sits outside the ancestor chain of the detected root will never be found.
Checks
astwire <target> --analysis
# base_dir shown in the printed table reveals the resolved rootResolution
Ensure one of the recognized marker files exists at your intended project root, and that .astwire.toml lives at or above that root in the directory tree.
6. Secret Not Redacted
Symptom
A credential appears unredacted in generated output.
Cause
redact_secrets() only matches four specific patterns — see ../architecture/security-and-privacy.md#2-secret-redaction. A credential under a non-matching variable name, split across lines, or not quoted in the expected key = "value" / key: "value" shape will not be caught.
Resolution
Do not treat astwire's redaction as a complete secret scanner. Review generated output manually before sharing it externally, and prefer excluding files known to contain credentials via .gitignore/.astwireignore/skip rather than relying on redaction alone.
7. --analysis Output Looks Wrong / Token Counts Differ From Expectations
Symptom
Token counts in --analysis output, or partition boundaries under --max-tokens, differ from what another tool (or a previous astwire run on a different machine) reported.
Cause
count_tokens() uses tiktoken's cl100k_base encoding when the tiktoken package is importable and initializes successfully; otherwise it falls back to a calibrated heuristic (len(text) / 3.7). The compiled binary may or may not bundle tiktoken depending on how it was compiled — the two paths produce different counts for the same content.
Checks
python3 -c "import tiktoken; print(tiktoken.get_encoding('cl100k_base'))"Resolution
For consistent counts across environments, ensure all environments either have tiktoken available or all rely on the fallback — mixing the two will produce slightly different partition boundaries for the same --max-tokens value. This is a precision difference, not a correctness bug; both paths produce a usable approximation.
8. Nuitka Compilation Fails Locally
Symptom
error: command 'gcc' failedor Nuitka reports missing patchelf.
Checks
which gcc g++ patchelf
python3 -m nuitka --versionResolution
Install the native toolchain:
# Debian/Ubuntu
apt-get update && apt-get install -y gcc g++ patchelf
pip install -U nuitka zstandardSee ../deployment/local-development.md#4-local-native-compilation-macos-arm64 for the full local compilation workflow, and ../deployment/binary-distribution.md for the Docker-based cross-platform build containers (which pin their own toolchain, avoiding host-machine dependency drift).
9. Windows PATH Not Updated After Install
Symptom
astwire --version reports "command not found" immediately after irm https://install.djangoplay.org/astwire.ps1 | iex succeeds.
Cause
install.ps1 updates the user PATH environment variable via [Environment]::SetEnvironmentVariable(...), but this does not propagate to already-open terminal sessions other than the one that ran the installer (and even that session's update is applied by appending to $env:Path directly, which some shells/terminals don't immediately re-evaluate for tab completion, etc.).
Resolution
Open a new terminal window/tab, then verify:
astwire --version10. astwire --uninstall Requires Elevated Permissions
Symptom (macOS/Linux)
Elevated permissions required. Running: sudo rm -f /usr/local/bin/astwirefollowed by a sudo password prompt, or a failure if sudo is unavailable/non-interactive.
Cause
/usr/local/bin/astwire is typically owned by root (since installation itself required sudo). perform_uninstall() first attempts a direct unlink(); on PermissionError it falls back to shelling out to sudo rm -f.
Resolution
Run interactively so the sudo password prompt can be answered, or run the manual uninstall commands from ../user-guide/installation.md#manual-uninstall with your own elevation mechanism (e.g. in a CI job with pre-authorized sudo).
11. macOS Blocks the Downloaded Binary (Gatekeeper)
Symptom
"astwire" cannot be opened because the developer cannot be verified.Cause
The compiled binary is not notarized/code-signed by Apple. macOS Gatekeeper quarantines binaries downloaded via curl.
Resolution
xattr -d com.apple.quarantine /usr/local/bin/astwireOr, on first run, allow it via System Settings → Privacy & Security → Allow Anyway.
12. Config File Silently Ignored
Symptom
Values in .astwire.toml appear to have no effect, with no error printed.
Cause
This is expected behavior by design: load_config() wraps tomllib.load() in a bare try/except Exception: return config — a malformed TOML file, wrong file location, or any parse error results in silent fallback to defaults, not a visible error.
Checks
python3 -c "import tomllib; tomllib.load(open('.astwire.toml', 'rb'))"Run this to surface the actual parse error load_config() is swallowing.
Resolution
Fix the TOML syntax, and confirm the file is discoverable from the resolved project root (see Section 5).
13. astwire: --since ... Errors
Symptom
One of:
astwire: --since accepts at most one scope path (got 2); pass one directory or file.
astwire: --since requires a git repository; '<path>' is not inside one.
astwire: --since: invalid git ref '<ref>'.
astwire: --since <ref>: no changed files under <path>.
astwire: --since <ref> found N changed file(s) under <path>, but all were excluded by --lang, skip rules, or .gitignore/.astwireignore.Exit code 2 for the first three (scope/ref problems); exit code 1 for the last two (nothing to bundle).
Cause
--since seeds strictly from git diff --name-only <ref> plus git ls-files --others under a single scope path — it never guesses a base branch, resolves a remote, or falls back to anything if the ref or scope is wrong. The two "found nothing" cases are deliberately distinguished: no changes at all under the scope, versus real changes that every one of --lang/skip/.gitignore/.astwireignore then filtered out.
Checks
git rev-parse --show-toplevel # confirms the scope is inside a git repo
git rev-parse --verify <ref> # confirms the ref exists
git diff --name-only <ref> -- <scope> # what --since is actually seeding fromResolution
Pass exactly one directory or file as the scope, and a REF that resolves in that repository (a branch, tag, HEAD~N, or commit SHA — never origin/main unless that remote-tracking ref actually exists locally). If files did change but got filtered, check --lang, .astwire.toml's skip list, and .gitignore/.astwireignore for the scope.
Common Recovery Matrix
| Symptom | First check | Typical action |
|---|---|---|
| No matching files found | .gitignore/.astwireignore/skip |
Adjust exclusions or target path |
| No registered files found | File extensions at target (astwire --languages) |
Point at directories containing registered file types |
-i misses expected files |
ast.parse()/engine parsing on suspect files; skip/.gitignore |
Fix SyntaxError; confirm project root; check exclusions |
Import target excluded by .gitignore/skip |
Resolution mode | Expected — skip/ignore win over -i, per SPEC-1.0.md §5 |
| Config not applying | find_project_root / .astwire.toml location |
Move config to/above detected root |
| Secret not redacted | Pattern match in redact.py |
Manual review; don't rely on redaction alone |
| Token counts differ across machines | tiktoken availability |
Align environments, or accept approximation |
| Nuitka build fails | gcc/g++/patchelf presence |
Install native toolchain, or use Docker build |
Windows astwire not found post-install |
Terminal session | Open a new terminal |
--uninstall needs a password |
Binary ownership | Run interactively, or use manual uninstall |
| macOS refuses to run the binary | Gatekeeper quarantine | xattr -d com.apple.quarantine |
.astwire.toml has no effect |
TOML syntax validity | Parse manually with tomllib to surface the error |
--since errors or finds nothing |
Scope is a git repo; REF resolves; --lang/skip/.gitignore |
Fix scope/ref, or loosen filters |
Escalation
When reporting a reproducible issue, capture:
astwire --version
python3 --versionAlong with the exact command, the full stderr/stdout, and (if relevant) the .astwire.toml/.astwireignore contents. Never include real secrets from generated output in a bug report — redact them manually first, since astwire's own redaction is best-effort (see Section 6).