# Path alias imports resolve to actual files

- **Pattern:** `ab-000023` (`ai-slop-hallucinations.module-references.path-aliases-resolve`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-20
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000023
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000023)

## Why it matters

Path-alias hallucinations are one of the most common AI failure modes: the model writes `import { calculateTax } from '@/lib/billing/calculator'` for a file it imagined rather than a file that exists. The TypeScript compiler and the bundler both fail at the resolution step, so the build breaks and the route cannot render. Even when `tsc --noEmit` is skipped in CI, runtime imports via dynamic `import()` throw `Cannot find module` the first time the code path executes in production.

## Severity rationale

High because an unresolved alias breaks the build or crashes the route at runtime, though a passing `tsc` step catches it before deploy.

## Remediation

Run `tsc --noEmit` to surface every unresolved alias, then for each one decide whether to create the missing file or correct the import specifier. Fix the reference in place:

```ts
// Correct: points at a file that actually exists under src/lib/billing/
import { calculateTax } from '@/lib/billing/tax'
```

If the alias itself is wrong, update `tsconfig.json` `compilerOptions.paths` so the prefix maps to the real directory.

## Detection

- **ID:** `path-aliases-resolve`
- **Severity:** `high`
- **What to look for:** Read `tsconfig.json` and any `tsconfig.*.json` files. Extract `compilerOptions.paths` to build the alias map (e.g., `"@/*": ["./src/*"]`). For every import in source files whose specifier matches an alias prefix, resolve the alias to the target directory and verify the resolved path points to an actual file. Apply standard extension resolution: try `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, then try `index.ts`, `index.tsx`, `index.js`, `index.jsx` if the resolved path is a directory. Count all alias imports inspected, total resolved, total unresolved.
- **Detector snippet (Node-capable tools only):** If the tool has a shell with Node available, parse `tsconfig.json` paths, scan every source file for imports matching an alias prefix, and resolve each target against the filesystem. Output reports the total alias imports and the count + identity of the unresolved ones. If exit 0 with "unresolved=0", report pass. If "unresolved>0", report fail with count. Exit >=2 or command not found — fall back to prose reasoning below.

  ```detect-node
  node -e 'const fs=require("fs"),path=require("path");const ts=JSON.parse(fs.readFileSync("tsconfig.json","utf-8"));const paths=(ts.compilerOptions&&ts.compilerOptions.paths)||{};const base=(ts.compilerOptions&&ts.compilerOptions.baseUrl)||".";function w(d,o=[]){try{for(const e of fs.readdirSync(d,{withFileTypes:true})){if(e.name==="node_modules"||e.name===".next")continue;const p=path.join(d,e.name);e.isDirectory()?w(p,o):o.push(p)}}catch{}return o}let t=0,u=[];for(const f of w("src")){if(!/\.(ts|tsx|js|jsx|mjs)$/.test(f))continue;const s=fs.readFileSync(f,"utf-8"),imp=[...s.matchAll(/from\s+[\u0022\u0027]([^\u0022\u0027]+)[\u0022\u0027]/g)].map(m=>m[1]);for(const i of imp){for(const [k,vs] of Object.entries(paths)){const pre=k.replace(/\*$/,"");if(!i.startsWith(pre))continue;t++;const rest=i.slice(pre.length);let ok=false;for(const v of vs){const rp=path.resolve(base,v.replace(/\*$/,rest));for(const ext of ["",".ts",".tsx",".js",".jsx","/index.ts","/index.tsx"]){if(fs.existsSync(rp+ext)){ok=true;break}}if(ok)break}if(!ok)u.push(f+": "+i);break}}}console.log("total="+t+" unresolved="+u.length);u.slice(0,3).forEach(x=>console.log(x))'
  ```

- **Pass criteria:** 100% of alias-prefixed imports resolve to an existing file (with extension) or directory containing an `index` file. Report: "X alias imports inspected, Y resolved, 0 unresolved."
- **Fail criteria:** At least 1 alias-prefixed import resolves to a non-existent path after extension resolution.
- **Do NOT pass when:** The alias map is empty but imports use alias-style paths (`@/lib/foo` with no `@/*` alias defined) — those should be flagged as unresolved.
- **Skip (N/A) when:** `tsconfig.json` does not exist OR `tsconfig.json` does not define `compilerOptions.paths`.
- **Report even on pass:** Always report the count. Example: "147 alias imports inspected (alias prefixes: '@/', '~/'), 147 resolved (100%)."
- **Detail on fail:** `"5 alias imports do not resolve: '@/lib/billing/calculator' in src/app/checkout/page.tsx, '@/components/MissingDialog' in src/app/page.tsx (file not found in src/components/)"`
- **Remediation:** Path alias hallucinations are a top AI failure mode — the model imagines a `@/lib/utils` helper that doesn't exist. Fix each unresolved alias import:

  ```ts
  // Bad: AI imagined this file
  import { calculateTax } from '@/lib/billing/calculator'

  // Good: import from a file that actually exists, or create the file
  import { calculateTax } from '@/lib/billing/tax'
  ```

  Run `tsc --noEmit` to surface module-resolution errors at build time. If a file should exist, create it. If the import was wrong, fix the path. If the alias is misconfigured, update `tsconfig.json` `compilerOptions.paths`.

Taxons: reference-integrity

HTML version: https://auditbuffet.com/patterns/ab-000023
