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

  1. Change the string id filter to a RegExp that matches the specifier (e.g. id: /^some-module$/ or id: /some-module/)
  2. Move the exact-match logic into the resolveId handler body instead of the filter
  3. 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

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


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)