Repository navigation
Description
Have you used AI?
Yes
Which part of webpack is affected?
module-federation, runtime (emitted runtime, chunk loading)
Bug Description
Since 5.111.0, a plugin runtime module that resolves its own chunk's filename
template fails the build when output.filename contains a hash placeholder:
Path variable [contenthash:8] not implemented in this context:
static/js/[name].[contenthash:8].js
RuntimeModule.updateHash calls generate() during Compilation.createHash, before
the chunk's contenthash exists. A generate() that calls compilation.getPath() on
the chunk filename template therefore resolves [contenthash:8] to undefined and
TemplatedPathPlugin throws.
Up to 5.110.3 that throw was caught in RuntimeModule.updateHash and the error
message hashed instead. 5.111.0 removed the catch, so it now propagates and fails
the compilation.
I understand why the catch was removed — hashing the error message pins the
codegen cache etag to a constant and can serve stale code, so it was masking a
real correctness problem. Two things still seem worth addressing:
-
The error doesn't name the runtime module or plugin that triggered it. It only
shows the filename template, which points the user at their own output.filename
and suggests their config is wrong when it isn't. This is very hard to trace —
I only found it by patching TemplatedPathPlugin to print a stack. -
RuntimeTemplate.chunkRootOutputDir() is the correct replacement and does fix it
(verified on 5.111.1), but it was only added in 5.110.0 — one minor before the
safety net was removed. A plugin supporting a range of webpack 5 versions has
no single call that works across them and needs a feature check.
This currently reaches users through @module-federation/enhanced (through 2.9.0),
where any CRA-style output.filename with [contenthash] fails to build on webpack
= 5.111.0. The repro below uses no Module Federation — it's a ~30 line plugin.
Link to Minimal Reproduction and steps to reproduce
Minimal repro, no Module Federation involved — a plugin runtime module that computes the undo path back to the output root, exactly as AutoPublicPathRuntimeModule does:
plugin.js
const { RuntimeModule, RuntimeGlobals } = require("webpack"); const JavascriptModulesPlugin = require("webpack/lib/javascript/JavascriptModulesPlugin"); const { getUndoPath } = require("webpack/lib/util/identifier");class MyRuntimeModule extends RuntimeModule {
constructor() { super("my runtime", 10); }
generate() {
const { compilation, chunk } = this;
const dir = getUndoPath(
compilation.getPath(
JavascriptModulesPlugin.getChunkFilenameTemplate(chunk, compilation.outputOptions),
{ chunk, contentHashType: "javascript" }
),
compilation.outputOptions.path || "",
false
);
return__webpack_require__.myRootDir = ${JSON.stringify(dir)};;
}
}
module.exports = class MyPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("MyPlugin", (compilation) => {
compilation.hooks.additionalTreeRuntimeRequirements.tap("MyPlugin", (chunk, set) => {
set.add(RuntimeGlobals.ensureChunkHandlers);
compilation.addRuntimeModule(chunk, new MyRuntimeModule());
});
});
}
};
webpack.config.js
module.exports = {
mode: "production",
entry: "./src/index.js",
output: { filename: "static/js/[name].[contenthash:8].js", publicPath: "auto" },
optimization: { runtimeChunk: false },
plugins: [new (require("./plugin.js"))()],
};
src/index.js only needs import("./lazy.js"); plus a trivial src/lazy.js (an async chunk, so ensureChunkHandlers is required).
Results:
| webpack | result |
|---|---|
| 5.110.3 | build OK, emits myRootDir="../../" |
| 5.111.0 | Path variable [contenthash:8] not implemented in this context |
| 5.111.1 | same |
Bisected to 5.111.0 — the removal of the try/catch in RuntimeModule.updateHash.
Stack trace
fn (lib/TemplatedPathPlugin.js:333)
Compilation.getAssetPath (lib/Compilation.js:6086)
Compilation.getPath (lib/Compilation.js:6058)
MyRuntimeModule.generate
RuntimeModule.getGeneratedCode (lib/RuntimeModule.js:242)
RuntimeModule.updateHash (lib/RuntimeModule.js:146)
Compilation._createModuleHash (lib/Compilation.js:5150)
Compilation.createHash (lib/Compilation.js:5402)Expected Behavior
Either the call keeps working as it did through 5.110.3, or the thrown error
identifies which runtime module triggered it so the responsible plugin can be found.
Actual Behavior
The build fails with:
Path variable [contenthash:8] not implemented in this context: static/js/[name].[contenthash:8].js
Stack:
fn lib/TemplatedPathPlugin.js:333
Compilation.getAssetPath lib/Compilation.js:6086
Compilation.getPath lib/Compilation.js:6058
MyRuntimeModule.generate plugin.js
RuntimeModule.getGeneratedCode lib/RuntimeModule.js:242
RuntimeModule.updateHash lib/RuntimeModule.js:146
Compilation._createModuleHash lib/Compilation.js:5150
Compilation.createHash lib/Compilation.js:5402
Environment
System:
OS: Linux 6.6 Ubuntu 24.04.3 LTS (Noble Numbat)
CPU: (4) x64 Intel(R) Core(TM) Ultra 7 255H
Memory: 2.64 GB / 11.68 GB
Binaries:
Node: 24.13.0
Yarn: 1.22.22
npm: 11.6.2
Packages:
webpack: ^5.111.1 → 5.111.1Is this a regression?
Yes (please specify version below)
Last Working Version
5.110.3
Additional Context
Bisected:
5.110.3 build OK, emits myRootDir="../../"
5.111.0 fails
5.111.1 fails
Verified fix on 5.111.1 — replacing the getPath call with
compilation.runtimeTemplate.chunkRootOutputDir(chunk, false) builds and emits the
correct myRootDir="../../".
Not Node-dependent: reproduces on Node 20 and 24.
Workaround for affected users: pin webpack to ~5.110.3.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Effort
Activity
fix: name the runtime module whose generation fails while hashing (#2…