tailwindlabs/tailwindcss · error · Error
`source(…)` paths must be quoted.
Error message
`source(…)` paths must be quoted.
What it means
Thrown while parsing the parameters of a legacy `@tailwind utilities` directive when a `source(…)` argument is not wrapped in matching quotes. The parser explicitly allows `source(none)` unquoted, but every actual path must be a quoted string literal (`source('...')` or `source("...")`), otherwise the parser cannot safely distinguish it from a keyword.
Source
Thrown at packages/tailwindcss/src/index.ts:209
let params = segment(node.params, ' ')
for (let param of params) {
if (param.startsWith('source(')) {
let path = param.slice(7, -1)
// Keyword: `source(none)`
if (path === 'none') {
root = path
continue
}
// Explicit path: `source('…')`
if (
(path[0] === '"' && path[path.length - 1] !== '"') ||
(path[0] === "'" && path[path.length - 1] !== "'") ||
(path[0] !== "'" && path[0] !== '"')
) {
throw new Error('`source(…)` paths must be quoted.')
}
root = {
base: (ctx.context.sourceBase as string) ?? (ctx.context.base as string),
pattern: path.slice(1, -1),
}
}
}
utilitiesNode = node
features |= Features.Utilities
}
// Collect custom `@utility` at-rules
if (node.name === '@utility') {
if (ctx.parent !== null) {
throw new Error('`@utility` cannot be nested.')
}View on GitHub (pinned to 16e94cbf7f)
Solutions
- Wrap the path in matching single or double quotes: `@tailwind utilities source('./src/**/*.html')`.
- Use `source(none)` (unquoted, the only permitted bareword) if you want to disable automatic source detection.
- Remove the legacy `@tailwind utilities` directive entirely and use `@source` at-rules (the v4 idiom) for explicit candidate scanning.
Example fix
/* before */
@tailwind utilities source(./src/**/*.html);
/* after */
@tailwind utilities source('./src/**/*.html'); Defensive patterns
Strategy: validation
Validate before calling
// Validate every source(...) argument in a @tailwind utilities directive.
const SOURCE_ARG = /source\(([^)]*)\)/g
function validateUtilitiesSource(directive: string): string[] {
const errors: string[] = []
for (const m of directive.matchAll(SOURCE_ARG)) {
const inner = m[1].trim()
if (inner === 'none') continue
if (!(inner.startsWith('"') && inner.endsWith('"')) &&
!(inner.startsWith("'") && inner.endsWith("'"))) {
errors.push(`source(${inner}) must be quoted`)
}
}
return errors
} Prevention
- Prefer the v4 `@source` at-rule over the legacy `@tailwind utilities source(...)` form.
- Add a lint rule (stylelint) that flags unquoted arguments inside `source(...)`.
- When migrating v3 files, mechanically wrap every `source(...)` path in single quotes.
When it happens
Trigger: Writing `@tailwind utilities source(./src/**/*.html)` (unquoted), or using mismatched quotes like `source("./src')`. Fires per `source(…)` segment inside the directive params.
Common situations: Upgrading a v3 stylesheet that used `@tailwind utilities` with `source(...)` and dropping the quotes during the edit. Copy-pasting glob examples from docs that stripped quotes in formatting.
Related errors
AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12).
Data as JSON: /api/errors/59adfa2c298a7a6a.
Report an issue: GitHub.