withastro/astro · warning

A resource was added to `${specific}`, but `${general}` also

Error message

A resource was added to `${specific}`, but `${general}` also defines custom resources (${defaultResources.join(' ')}). Because `${specific}` overrides `${general}` for its scope (browsers do not fall back), those resources will not apply there. Add them to `${specific}` as well if needed.

What it means

In Content-Security-Policy, specific directives (script-src-elem/-attr, style-src-elem/-attr) fully replace the general directive (script-src/style-src) for their scope — browsers do not merge or fall back. When a resource is added to a specific directive while the general one also lists custom default resources, Astro warns (deduped once per family+kind per request via the warnedFallback set) that the general resources will not apply in that scope.

Source

Thrown at packages/astro/src/core/fetch/fetch-state.ts:696

				family === 'script' ? state.result.scriptDirective : state.result.styleDirective;
			// Astro's element hashes are folded into the `-elem` directive automatically, so the
			// footgun is specifically user-provided `default`-kind resources on the general directive,
			// which do NOT carry over to the more specific directive.
			const defaultResources = directive.resources
				.map(normalizeCspResourceEntry)
				.filter((entry) => entry.kind === 'default')
				.map((entry) => entry.resource);
			if (defaultResources.length === 0) {
				return;
			}
			const key = `${family}:${kind}`;
			if (warnedFallback.has(key)) {
				return;
			}
			warnedFallback.add(key);
			const general = `${family}-src`;
			const specific = `${general}-${kind === 'element' ? 'elem' : 'attr'}`;
			state.logger.warn(
				'csp',
				`A resource was added to \`${specific}\`, but \`${general}\` also defines custom resources (${defaultResources.join(
					' ',
				)}). Because \`${specific}\` overrides \`${general}\` for its scope (browsers do not fall back), those resources will not apply there. Add them to \`${specific}\` as well if needed.`,
			);
		};
		return {
			insertDirective(payload) {
				if (state.result) {
					state.result.directives = pushDirective(state.result.directives, payload);
				}
			},
			insertScriptResource(payload) {
				if (!state.result) return;
				warnFallback('script', normalizeCspResourceEntry(payload).kind);
				state.result.scriptDirective.resources.push(payload);
			},
			insertStyleResource(payload) {

View on GitHub (pinned to e294953aa8)

Solutions

  1. Add the general directive's resources to the specific directive as well (repeat them for the elem/attr kinds)
  2. Or drop the custom defaults from the general directive so only the specific one carries resources
  3. Inspect the rendered Content-Security-Policy header to confirm the effective policy after the change

Example fix

// before — defaults on script-src get shadowed by script-src-elem additions
security: {
  csp: {
    scriptDirective: {
      resources: [
        { value: "'self'" },                         // kind 'default'
        { value: 'https://cdn.example.com', kind: 'elem' },
      ],
    },
  },
}

// after — repeat the defaults for the elem scope
security: {
  csp: {
    scriptDirective: {
      resources: [
        { value: "'self'" },
        { value: "'self'", kind: 'elem' },
        { value: 'https://cdn.example.com', kind: 'elem' },
      ],
    },
  },
}
Defensive patterns

Strategy: validation

Validate before calling

// mirror the browser's no-fallback rule before shipping CSP config
function checkCspShadowing(cfg) {
  for (const [dir, family] of [[cfg.scriptDirective, 'script'], [cfg.styleDirective, 'style']]) {
    const defaults = dir?.resources?.filter((r) => r.kind === undefined || r.kind === 'default') ?? [];
    const specifics = dir?.resources?.filter((r) => r.kind === 'elem' || r.kind === 'attr') ?? [];
    if (defaults.length && specifics.length) {
      console.warn(`${family}-src resources (${defaults.map((r) => r.value).join(' ')}) are shadowed by ${family}-src-elem/-attr`);
    }
  }
}

Prevention

When it happens

Trigger: security.csp configures custom resources on the general script-src/style-src directive (kind 'default'), and route code or config also adds resources with kind 'elem' or 'attr' — the specific directive then overrides the general one with no fallback.

Common situations: Mixing directive levels when allowing third-party scripts; configs that grew defaults first and per-element/per-attribute additions later; migrating policies copied from other frameworks.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/c87b89a54e677b80. Report an issue: GitHub.