tailwindlabs/tailwindcss · error · Error

The path given to `source(…)` must be a directory but got `s

Error message

The path given to `source(…)` must be a directory but got `source(${basePath})` instead.

What it means

The Webpack plugin performs the same `source(…)` directory validation as Turbopack: it `statSync`s the resolved path and throws if the entry exists but is not a directory. `ENOENT` is swallowed, so a not-yet-created directory is allowed (useful when the dir is generated during the build).

Source

Thrown at packages/@tailwindcss-webpack/src/index.ts:248

        // Skip negated patterns
        if (glob.pattern[0] === '!') continue

        // Avoid adding a dependency on the base directory itself
        if (glob.pattern === '*' && base === glob.base) {
          continue
        }

        this.addContextDependency(path.resolve(glob.base))
      }

      // Validate that source(...) paths are directories
      let root = compiler.root
      if (root !== 'none' && root !== null) {
        let basePath = normalizePath(path.resolve(root.base, root.pattern))
        try {
          let stats = fs.statSync(basePath)
          if (!stats.isDirectory()) {
            throw new Error(
              `The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`,
            )
          }
        } catch (err) {
          if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
            throw err
          }
          // Directory doesn't exist yet, which is fine
        }
      }
      DEBUG && I.end('Register dependency messages')
    }

    DEBUG && I.start('Build utilities')
    let css = compiler.build([...context.candidates])
    DEBUG && I.end('Build utilities')

    // Optionally optimize the output

View on GitHub (pinned to 16e94cbf7f)

Solutions

  1. Use a directory path or a glob pattern whose non-glob prefix is a directory.
  2. If the directory is build output, ensure it is created before the webpack build, or point `@source` at the source directory instead.
  3. Remove any file occupying the intended directory path.

Example fix

/* before */
@source "./pages/index.html";

/* after */
@source "./pages/**/*.html";
Defensive patterns

Strategy: validation

Validate before calling

import { statSync } from 'node:fs'
function assertSourceDirOrMissing(pattern: string, base: string) {
  const p = path.resolve(base, pattern)
  try {
    if (!statSync(p).isDirectory()) throw new Error(`source(${pattern}) is a file, not a directory`)
  } catch (e) { if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e }
}

Type guard

function isDirectory(p: string): boolean {
  try { return statSync(p).isDirectory() } catch { return false }
}

Prevention

When it happens

Trigger: `source("./dist/index.html")` where the path is a file, or any `@source` whose literal prefix resolves to an existing non-directory entry. index.ts:248 throws after `statSync` + `!isDirectory()`. (Missing paths do NOT throw.)

Common situations: Same-family mistake as the other bundler plugins: a file path used where a directory/glob is required, or a directory replaced by a same-named file.

Related errors


AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12). Data as JSON: /api/errors/02202f535e55cbce. Report an issue: GitHub.