Debug Symbol Resolution Issues
import { Aside, Tabs, TabItem } from ‘@astrojs/starlight/components’;
Symbol resolution issues appear as broken imports in forge health, missing results from forge_search_symbols, or forge_trace_imports returning empty when you know imports exist. This guide walks through the diagnostic tools and the common causes.
Step 1: Check what symbols were extracted
Section titled “Step 1: Check what symbols were extracted”Before diagnosing why a symbol can’t be found, confirm it was extracted at all.
Use forge_parse_file (MCP) or forge stats (CLI) to inspect what Forge sees in a specific file.
Via MCP:
Call forge_parse_file on src/lib/payments.tsExpected output:
forge_parse_file: src/lib/payments.ts
Exports: - createPayment (function, line 12) - PaymentConfig (interface, line 3) - PaymentResult (type alias, line 8) - DEFAULT_CURRENCY (const, line 1)
Imports: - ../models/cart (CartItem, CartTotal) → resolved: src/models/cart.ts - ../config/env (ENV) → resolved: src/config/env.ts - stripe (Stripe) → resolved: node_modules/stripe (external, not indexed)
Symbol count: 4 exports, 3 importsIf the symbol you’re looking for is NOT in the exports list, Forge never extracted it. This could mean:
- The file has a syntax error that prevented parsing
- The export uses an unusual syntax Forge doesn’t recognize
- The file is in an
ignored_pathsdirectory
Step 2: Check import resolution
Section titled “Step 2: Check import resolution”Broken imports appear in forge health as P0 findings. To inspect why a specific import doesn’t resolve, use forge_trace_imports:
Via MCP:
Call forge_trace_imports on src/api/orders.tsExpected output for a working file:
forge_trace_imports: src/api/orders.ts
Direct imports (4): ../lib/payments → RESOLVED → src/lib/payments.ts ../models/order → RESOLVED → src/models/order.ts ../utils/validate → RESOLVED → src/utils/validate.ts @stripe/stripe-js → EXTERNAL (not indexed)A broken import looks like:
../lib/stripe-client → UNRESOLVED Attempted paths: src/lib/stripe-client.ts — not found src/lib/stripe-client/index.ts — not found src/lib/stripe-client.js — not found“Not found” means the file doesn’t exist at that path. Either the import is wrong, the file was moved, or there’s a path alias that Forge doesn’t know about.
Step 3: Check path alias configuration
Section titled “Step 3: Check path alias configuration”Path aliases are a common source of broken imports. If your TypeScript project uses @app/*, @shared/*, or ~/*, Forge needs to know how to resolve them.
First, check if Forge already reads your tsconfig.json:
FORGE_LOG=debug forge index . 2>&1 | grep "tsconfig"If Forge found and read your tsconfig.json, aliases defined in compilerOptions.paths are resolved automatically.
If aliases still don’t resolve after indexing, add them explicitly to .forge/config.toml:
[resolve]aliases = [ { alias = "@app", target = "src" }, { alias = "@shared", target = "packages/shared/src" }, { alias = "~", target = "src" },]Re-run forge index . after adding aliases. Then re-run forge_trace_imports to verify the imports resolve.
Step 4: Check module reachability
Section titled “Step 4: Check module reachability”forge_check_wiring tells you whether a module is reachable from any entry point:
Via MCP:
Call forge_check_wiring on src/lib/payments.tsExpected output when reachable:
forge_check_wiring: src/lib/payments.ts
Reachable: YES Entry point: src/index.ts Path: src/index.ts → src/api/orders.ts → src/lib/payments.ts (3 hops)Expected output when unreachable:
forge_check_wiring: src/lib/old-payments.ts
Reachable: NO No path from any entry point to this file. Imported by: 0 files Note: this file may be dead code or a new module not yet wired in.Unreachable modules aren’t necessarily broken — they might be utilities called at runtime via dynamic import, new modules not yet connected, or intentionally standalone scripts. Use context to determine if “unreachable” is a real problem.
Step 5: Search for a symbol by name
Section titled “Step 5: Search for a symbol by name”If you know a symbol exists but can’t find it in Forge’s results:
forge search --symbols "createPayment"Or via MCP:
Call forge_search_symbols with query="createPayment"If the symbol doesn’t appear, it wasn’t extracted. Go back to Step 1 and check forge_parse_file for the file you expect it to be in.
Step 6: Run the full health check
Section titled “Step 6: Run the full health check”forge healthLook for:
broken_import— an import that doesn’t resolve to any fileunresolved_module— a module-level import that Forge couldn’t findmissing_export— a symbol is imported somewhere but not exported from the source
These findings point directly to resolution failures.
Common issues and fixes
Section titled “Common issues and fixes”TypeScript barrel files (index.ts re-exports)
If a file re-exports symbols from other modules with export * from './payments', Forge tracks these re-exports. But if the barrel file itself is in ignored_paths or has a parse error, downstream imports that go through it appear broken.
Check the barrel file:
Call forge_parse_file on src/lib/index.tsIf it shows no exports, the barrel file isn’t parsing correctly. Check for syntax errors.
Dynamic imports
import(...) is a dynamic import and Forge tracks it separately from static imports. forge_trace_imports includes dynamic imports in its output, but they’re marked as DYNAMIC:
import('../plugins/' + pluginName) → DYNAMIC (unresolvable at index time)Dynamic imports with variable paths can’t be resolved statically — this is expected, not a bug.
Non-standard import syntax
Some tools and frameworks use non-standard import syntax (Webpack magic comments, Vite’s import.meta.glob, etc.). Forge may not recognize these as imports. If a widely-used file shows 0 imports in forge_parse_file, it likely uses custom syntax.
Workaround: add the file to ignored_paths if it’s causing false positive health findings, or use forge_search to find references manually.
forge_trace_dependents returns fewer results than expected
forge_trace_dependents returns only files that statically import the target. Files that reference it via a string path (e.g., require('./payments') in a config file, or a dynamic import(path)) don’t appear. This is a limitation of static analysis, not a bug.