--- since: 1.0.0 --- # 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/`). --- ## 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 cat .astwire.toml 2>/dev/null cat .astwireignore 2>/dev/null git check-ignore -v / ``` ### 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 -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](#4-import-resolution-and-gitignore--astwireignore) 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](#5-project-root-or-config-is-not-what-you-expect). 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 --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`](../architecture/security-and-privacy.md). 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`](../deployment/local-development.md) for the full local compilation workflow, and [`../deployment/binary-distribution.md`](../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`](../user-guide/installation.md) 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](#5-project-root-or-config-is-not-what-you-expect)). --- ## 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; '' is not inside one. astwire: --since: invalid git ref ''. astwire: --since : no changed files under . astwire: --since found N changed file(s) under , 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 ` 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 # confirms the ref exists git diff --name-only -- # 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](#6-secret-not-redacted)).