A deploy stops with a single line: LWC1010: Failed to resolve entry for module "c/shipmentETAPanel". The bundle is right there in the source tree, the editor shows no squiggles, and the message names no file. The error is narrower than it reads — the resolver found a directory to look in and found nothing inside it that it would accept as an entry. Once you know which two names it compared, the fix is a rename, and it takes about a minute.
What LWC1010 is actually reporting
The code is not a platform mystery; it sits in the compiler's own error table in the open-source @lwc/errors package:
IMPORTEE_RESOLUTION_FAILED: {
code: 1010,
message: 'Failed to resolve entry for module "{0}".',
level: DiagnosticLevel.Error,
strictLevel: DiagnosticLevel.Fatal,
}
Two details matter. strictLevel: DiagnosticLevel.Fatal means there is no partial build to inspect — the compile stops. And {0} is the module specifier, which is the name the resolver derived, not necessarily the string you typed. If your template says <c-shipment-e-t-a-panel>, the specifier in the error reads c/shipmentETAPanel. That derived name is the most useful thing in the message: it is the folder and the file the resolver went looking for.
The lookup itself is unglamorous. In @lwc/module-resolver, once a specifier has been split into namespace and name and matched against a configured module directory, the entry is found by joining the directory path with the module name and trying extensions in a fixed order:
// getModuleEntry, paraphrased from @lwc/module-resolver/src/utils.ts
for (const ext of ['js', 'ts', 'html', 'css']) {
const entry = path.join(moduleDir, `${moduleName}.${ext}`);
if (fs.existsSync(entry)) return entry;
}
throw new LwcConfigError(`Unable to find a valid entry point for "${moduleDir}/${moduleName}"`, ...);
That is a plain string join against the filesystem. No fuzzy matching, no case folding, no fallback to "the only .js file in the folder". If the folder is shipmentEtaPanel and every file in it is spelled shipmentETAPanel, all four joins produce paths that do not exist on any filesystem that takes case seriously.
It is worth separating this from its neighbour. If no configured module record matches the specifier — a missing modules entry in lwc.config.json, a component outside your package directories — the resolver throws NO_LWC_MODULE_FOUND instead. So when the message says entry, the directory was found. Do not go and rewrite your resolver config.
Cause 1: the tag does not map back to a folder that exists
Salesforce documents the mapping in one sentence: camelCase folder names map to kebab-case in markup, so myComponent renders as <c-my-component>. The compiler runs that mapping in reverse — it reads the tag, rebuilds the camelCase name, and asks the filesystem for it.
Here is a parent that gets it wrong. The bundle folder on disk is shipmentETAPanel:
<!-- carrierScorecard.html -->
<template>
<lightning-card title="Carrier Performance">
<c-shipment-eta-panel shipment-id={recordId}></c-shipment-eta-panel>
</lightning-card>
</template>
That tag reverses to shipmentEtaPanel, which is not a folder. Every capital gets its own hyphen, acronyms included:
<c-shipment-e-t-a-panel shipment-id={recordId}></c-shipment-e-t-a-panel>
Acronyms are where this bites, and where the autocomplete in VS Code will not save you — it is happy to insert a tag that the compiler will reject. I have watched a team spend most of an afternoon on a <c-e-d-i-batch-monitor> tag because everyone reading the code assumed the hyphens were a typo and kept "fixing" them back.
My position: do not ship shipment-e-t-a-panel. Rename the bundle to shipmentEtaPanel so the markup reads as a human would write it, and keep the rule "one capital per word, never per letter" in your standards doc. The trade-off is real and you pay it once — the folder, all four or five files inside it, the .js-meta.xml, every template that references the tag, every Jest import path, and any Lightning page or Experience Cloud page the old component was placed on all have to move together. That is an hour of coordinated churn against years of people mistyping the tag.
One related trap while you are in the naming rules: underscores are legal in folder names but do not participate in the mapping. A folder called shipment_panel is referenced as <c-shipment_panel>, not <c-shipment-panel>. Hyphens, meanwhile, are not allowed in folder or file names at all, so you cannot sidestep the mapping by naming the folder after the tag. Attribute names follow the same camel-to-kebab rule as tags — the shipmentId public property is set as shipment-id — but a wrong attribute name gives you an undefined property, not LWC1010.
Cause 2: the folder and its files disagree on case
This is the one that ships green from a laptop and detonates in CI. It usually starts as a rename that only half happened: someone changes the folder name in the editor's file tree and leaves the files inside alone.
force-app/main/default/lwc/shipmentEtaPanel/
├── shipmentETAPanel.js # folder now says Eta; every file still says ETA
├── shipmentETAPanel.html
└── shipmentETAPanel.js-meta.xml
macOS and Windows filesystems are case-insensitive by default, so the join finds shipmentETAPanel.js locally and the bundle compiles. A Linux build agent compares byte for byte. It tries shipmentEtaPanel.js, then .ts, then .html, then .css, and none of those paths exist — the .js-meta.xml was never a candidate extension in the first place. The scan ends with nothing to return, and that is LWC1010.
Rename half the files instead of none and you get a stranger failure; that one is in "What to watch for" below.
The platform compiler carries a dedicated diagnostic for the case-mismatch shape, and if you get it you are lucky, because it names both sides:
{
"code": 1117,
"message": "Failed to resolve \"{0}\". The file name must case match the component folder name \"{1}\"."
}
Getting LWC1010 instead of LWC1117 usually means git is the problem rather than the compiler. A case-insensitive checkout will happily keep the old filename in the index after you rename it in the editor, so the repository still contains shipmentETAPanel.js even though your working tree looks correct. Force the rename through a neutral name:
cd force-app/main/default/lwc/shipmentEtaPanel
git mv shipmentETAPanel.js tmp-entry.js
git mv tmp-entry.js shipmentEtaPanel.js
git status --short # expect a rename, not "nothing to commit"
If git status reports nothing after the first git mv, that is your confirmation the repo never had the name you thought it had.
Read the code number, not the sentence
Four of these errors share the phrase "Failed to resolve" and they send you to four different files. The number is the only reliable signal:
| Code | Message | What is wrong | Where to fix it |
|---|---|---|---|
| LWC1002 | Error in module resolution: {0} |
Resolver could not run at all | lwc.config.json / package dirs |
| LWC1004 | No such file {0} |
A path was resolved but is absent | The named path |
| LWC1010 | Failed to resolve entry for module "{0}". |
Folder found, no entry file under that name | Tag name, or the files inside the bundle |
| LWC1011 | Failed to resolve import "{0}" from "{1}". Please add "{2}" file to the component folder. |
An import points inside a bundle that lacks the file | Add the file it names |
| LWC1116 | Illegal folder name "{0}". The folder name must start with a lowercase character: "{1}". |
Bundle folder is PascalCase | Rename the folder |
| LWC1117 | Failed to resolve "{0}". The file name must case match the component folder name "{1}". |
Folder and file cased differently | Rename the file |
| LWC1118 | Failed to resolve "{0}" from "{1}". Did you mean "{2}"? |
An import path is cased wrong | The import statement |
LWC1011 is the one most often misread as LWC1010. It fires when something imports a specific file — import LABEL from './messages' — and that file is soql-not-in-not-equal-exclusion/" class="auto-link">not in the bundle. It even tells you what to add. LWC1010 never names a file, because from the resolver's point of view no candidate file existed to name.
A check to run before you redeploy
Rather than eyeball a directory tree, make the machine compare the two names. This walks every bundle and reports any folder with no entry file matching the folder name exactly:
for dir in force-app/main/default/lwc/*/; do
name=$(basename "$dir")
if [ ! -f "$dir$name.js" ] && [ ! -f "$dir$name.ts" ] && [ ! -f "$dir$name.html" ]; then
echo "no entry matching folder name: $name"
ls "$dir"
fi
done
Wire that into a pre-commit hook or the first step of your CI job. It runs in well under a second on a few hundred bundles and it turns a ten-minute failed deploy into an instant local failure — which is the whole point, because the case bug is invisible on the machine where the code was written.
What to watch for
- The specifier in the message is derived from your markup, so a tag typo shows up as a plausible-looking folder name that never existed. Search the repo for it before assuming the folder was deleted.
- A half-finished rename is worse than none. Because the extension order is
.js,.ts,.html,.css, a bundle whose JS file is still mis-cased but whose HTML file has been corrected resolves successfully — to the HTML. You get no LWC1010, just a confusing "is not a constructor"-shaped failure further down the build. - The sweep above only proves an entry file exists under the folder's name. It will not catch that half-renamed bundle, because the correctly cased
.htmlsatisfies the test. Fix casing for the whole folder in one commit. - Renaming a bundle to fix the acronym mapping is a delete-and-recreate from the metadata side. Deploy the new name, repoint every reference, and remove the old bundle in a separate step so a half-applied deploy does not leave two live copies.
- Jest and the local dev server resolve through
@lwc/module-resolverrather than the platform compiler, so the same mistake can surface asUnable to find a valid entry point for ...orNO_LWC_MODULE_FOUND. Different wording, same rename. - A component that exists but sits outside your configured package directories gives
NO_LWC_MODULE_FOUND, not LWC1010. If you see that one, the naming is fine and the config is not.
Leave a Comment