rolldown/rolldown · error · Error
A string `id` filter is not supported for the
Error message
A string `id` filter is not supported for the `${hookName}` hook, because its `id` is the import specifier rather than a resolved path. Use a RegExp instead. What it means
For the resolveId hook the `id` being filtered is the raw import specifier, not a resolved filesystem path, so a string `id` filter would compare against unpredictable specifier text. Rolldown rejects this combination with this Error, advising a RegExp for partial/specifier matching; string ids remain valid for hooks whose id is a resolved path.
Solutions
- Change the string id filter to a RegExp that matches the specifier (e.g. id: /^some-module$/ or id: /some-module/)
- Move the exact-match logic into the resolveId handler body instead of the filter
- Keep string id filters only for hooks where id is a resolved path (load, transform, etc.)
Example fix
// before
resolveId: {
filter: { id: 'virtual:my-module' },
handler() { ... }
}
// after
resolveId: {
filter: { id: /^virtual:my-module$/ },
handler() { ... }
} Defensive patterns
Strategy: validation
Validate before calling
function validateResolveIdFilter(filter) {
if (typeof filter?.id === 'string') {
throw new Error('resolveId id filter must be a RegExp, not a string');
}
} Try / catch
try {
registerPlugin(pluginWithResolveId);
} catch (e) {
if (String(e).includes('string `id` filter')) {
console.error('Convert the string id filter to a RegExp for resolveId');
} else throw e;
} Prevention
- Use RegExp id filters for resolveId since ids are raw specifiers
- Keep exact string matching for resolved-path hooks (load/transform)
- Test plugin registration in a unit test so filter-shape errors surface before builds
When it happens
Trigger: Defining a plugin whose resolveId `filter` contains a string `id` (exact string matcher, e.g. id: 'some-module') — bindingifyResolveIdFilter calls assertNoStringId and throws at registration.
Common situations: Reusing a load/transform filter shape for resolveId; trying to exactly match a specifier by string, not realizing specifiers may be relative, aliased, or bare; migrating plugins between bundler APIs with different filter semantics.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- The `importerId` filter can only be used with the…
- `advancedChunks` option is deprecated, please use…
- `advancedChunks` option is ignored because `codeSplitting`…
- `advancedChunks` option is ignored because…
- `advancedChunks` option is ignored because the…
AI-assisted analysis of rolldown/rolldown@91b44b9d7b (2026-09-07).
Data as JSON: /api/errors/01d388b57e2e2793.
Report an issue: GitHub.
Appendix: source
Thrown at packages/rolldown/src/plugin/bindingify-hook-filter.ts:162
case 'or':
return expr.args.some(containsStringId);
case 'not':
case 'include':
case 'exclude':
return containsStringId(expr.expr);
case 'id':
return typeof expr.pattern === 'string';
default:
return false;
}
}
function assertNoStringId(
filterExprs: filter.TopLevelFilterExpression[] | undefined,
hookName: string,
): void {
if (filterExprs?.some(containsStringId)) {
throw new Error(
`A string \`id\` filter is not supported for the \`${hookName}\` hook, because its \`id\` is the import specifier rather than a resolved path. Use a RegExp instead.`,
);
}
}
function bindingifyFilterExprImpl(
expr: FilterExpression | TopLevelFilterExpression,
list: BindingFilterToken[],
) {
switch (expr.kind) {
case 'and': {
let args = expr.args;
for (let i = args.length - 1; i >= 0; i--) {
bindingifyFilterExprImpl(args[i], list);
}
list.push({
kind: 'And',
payload: args.length,View on GitHub (pinned to 91b44b9d7b)