{"record":{"id":"4e15619fee6435b3","repo":"swc-project/swc","slug":"module-specifier-matched-prefix-from-tsco","errorCode":null,"errorMessage":"`{module_specifier}` matched `{prefix}` (from tsconfig.paths) but failed to resolve:\n{errors:?}","messagePattern":"`(.+?)` matched `(.+?)` \\(from tsconfig\\.paths\\) but failed to resolve:\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/swc_ecma_loader/src/resolvers/tsc.rs","lineNumber":296,"sourceCode":"                            info!(\n                                \"Using `{}` for `{}` because the length of the jsc.paths entry is \\\n                                 1\",\n                                replaced, module_specifier\n                            );\n                            return Ok(Resolution {\n                                slug: Some(\n                                    replaced\n                                        .split([std::path::MAIN_SEPARATOR, '/'])\n                                        .next_back()\n                                        .unwrap()\n                                        .into(),\n                                ),\n                                filename: FileName::Real(replaced.into()),\n                            });\n                        }\n                    }\n\n                    bail!(\n                        \"`{module_specifier}` matched `{prefix}` (from tsconfig.paths) but failed \\\n                         to resolve:\\n{errors:?}\"\n                    )\n                }\n                Pattern::Exact(from) => {\n                    // Should be exactly matched\n                    if module_specifier != from {\n                        continue;\n                    }\n\n                    let tp = Path::new(&to[0]);\n                    let slug = to[0]\n                        .split([std::path::MAIN_SEPARATOR, '/'])\n                        .next_back()\n                        .filter(|&slug| slug != \"index.ts\" && slug != \"index.tsx\")\n                        .map(|v| v.rsplit_once('.').map(|v| v.0).unwrap_or(v))\n                        .map(From::from);\n","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_ecma_loader/src/resolvers/tsc.rs#L278-L314","documentation":"TsConfigResolver (tsc.rs) applies tsconfig `paths` patterns to a module specifier. When a pattern matches (prefix/wildcard), it substitutes the captured part into each substitution target and tries the inner resolver with the raw specifier, then './<replaced>' and '<replaced>' relative to baseUrl. If every candidate fails, it bails with this message, embedding the per-target resolution errors, so the specifier matched a paths mapping but none of the mapped destinations exist.","triggerScenarios":"tsconfig contains `paths: { \"@/*\": [\"src/legacy/*\"] } }` and an import like '@/foo' matches, but src/legacy/foo.ts does not exist (wrong baseUrl, moved directory, wrong casing, or target file truly missing). The `to.len() == 1` fallback only returns the replaced path directly when there is exactly one target; otherwise failure is fatal.","commonSituations":"baseUrl not set or pointing to the wrong directory so './replaced' resolves from the wrong root; paths aliases from a template not updated after a folder rename; monorepos where tsconfig lives in a package root but the build runs from the workspace root; multiple substitution targets where none exists; extension not covered by the resolver's substitution table.","solutions":["Check each substitution target in the matched paths entry actually exists on disk relative to baseUrl (mind the './' prefixing and case sensitivity)","Fix `baseUrl` in tsconfig so relative targets resolve from the intended root, or make targets absolute","Add or correct the missing file the alias points to, or update the alias after directory renames","Read the embedded per-target errors in the message: they tell you exactly which candidates the resolver attempted"],"exampleFix":"// before (tsconfig.json)\n{ \"baseUrl\": \"src\", \"paths\": { \"@/*\": [\"src/*\"] } }\n// '@/foo' -> tries src/src/foo -> fails\n\n// after\n{ \"baseUrl\": \".\", \"paths\": { \"@/*\": [\"src/*\"] } }\n// '@/foo' -> resolves ./src/foo.ts","handlingStrategy":"try-catch","validationCode":"// Verify every substitution target of the matched alias exists relative to baseUrl\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nfunction aliasTargetsExist(tsconfig, spec) {\n  for (const [pattern, targets] of Object.entries(tsconfig.compilerOptions.paths ?? {})) {\n    const prefix = pattern.split('*')[0];\n    if (spec.startsWith(prefix)) {\n      const rest = spec.slice(prefix.length);\n      const ok = targets.some((t) => existsSync(path.resolve(tsconfig.compilerOptions.baseUrl ?? '.', t.replace('*', rest))));\n      if (!ok) return false;\n    }\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const resolved = tsResolver.resolve(base, spec);\n} catch (e) {\n  if (e.message.includes('(from tsconfig.paths)')) {\n    // e.message lists every attempted target + inner errors; fix tsconfig or add the file\n    throw new Error(`tsconfig paths misconfigured for '${spec}': ${e.message}`);\n  }\n  throw e;\n}","preventionTips":["After any directory rename, grep tsconfig paths entries and confirm targets exist","Set baseUrl explicitly and test aliases with tsc --noEmit before bundling","Keep one source of truth for aliases (tsconfig) and derive bundler aliases from it"],"tags":["tsconfig-paths","module-resolution","path-alias","base-url","swc-ecma-loader"],"backgroundTag":"tsconfig-path-mapping-failed","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}