astwire / Runbooks / Troubleshooting
DocsastwireRunbooksTroubleshooting

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/).

11 min readApplies to v1.0.0
On this page ▾
  1. 1. astwire: No matching files found.
  2. Symptom
  3. Cause
  4. Checks
  5. Resolution
  6. 2. astwire: No matching files found (skipped N unregistered file(s)).
  7. Symptom
  8. Cause
  9. Resolution
  10. 3. -i Finds Fewer Files Than Expected
  11. Symptom
  12. Checks
  13. Resolution
  14. 4. Import Resolution and .gitignore / .astwireignore
  15. Symptom
  16. Cause
  17. Resolution
  18. 5. Project Root or Config Is Not What You Expect
  19. Symptom
  20. Cause
  21. Checks
  22. Resolution
  23. 6. Secret Not Redacted
  24. Symptom
  25. Cause
  26. Resolution
  27. 7. --analysis Output Looks Wrong / Token Counts Differ From Expectations
  28. Symptom
  29. Cause
  30. Checks
  31. Resolution
  32. 8. Nuitka Compilation Fails Locally
  33. Symptom
  34. Checks
  35. Resolution
  36. 9. Windows PATH Not Updated After Install
  37. Symptom
  38. Cause
  39. Resolution
  40. 10. astwire --uninstall Requires Elevated Permissions
  41. Symptom (macOS/Linux)
  42. Cause
  43. Resolution
  44. 11. macOS Blocks the Downloaded Binary (Gatekeeper)
  45. Symptom
  46. Cause
  47. Resolution
  48. 12. Config File Silently Ignored
  49. Symptom
  50. Cause
  51. Checks
  52. Resolution
  53. 13. astwire: --since ... Errors
  54. Symptom
  55. Cause
  56. Checks
  57. Resolution
  58. Common Recovery Matrix
  59. Escalation

1. astwire: No matching files found.

Symptom

text
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

bash
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

text
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 computed require(someVar)) — none of the engines evaluate expressions, only literal specifiers.
  • Python imports inside a try/except ImportError block 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() catches SyntaxError/ValueError/UnicodeDecodeError and 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 under node_modules/, or tsconfig.json path aliases — only relative (./, ../) specifiers are followed.
  • Go imports outside the current module (resolved via the nearest go.mod), or anything under GOROOT/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/.astwireignore would 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

bash
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

bash
astwire <target> --analysis
# base_dir shown in the printed table reveals the resolved root

Resolution

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

bash
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

text
error: command 'gcc' failed

or Nuitka reports missing patchelf.

Checks

bash
which gcc g++ patchelf
python3 -m nuitka --version

Resolution

Install the native toolchain:

bash
# Debian/Ubuntu
apt-get update && apt-get install -y gcc g++ patchelf

pip install -U nuitka zstandard

See ../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:

powershell
astwire --version

10. astwire --uninstall Requires Elevated Permissions

Symptom (macOS/Linux)

text
Elevated permissions required. Running: sudo rm -f /usr/local/bin/astwire

followed 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

text
"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

bash
xattr -d com.apple.quarantine /usr/local/bin/astwire

Or, 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

bash
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:

text
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

bash
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 from

Resolution

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:

bash
astwire --version
python3 --version

Along 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).