withastro/astro · error · AstroError

CacheNotEnabled

CacheNotEnabled

Error message

`Astro.cache` is not available because the cache feature is not enabled. To use caching, configure a cache provider in your Astro config under `cache`.

What it means

Astro.cache is backed by a provider supplied either by an adapter or by the cache config. The runtime object exists even without a provider, but cache operations — invalidate() here — throw CacheNotEnabled when no provider is configured, which is also the case in dev where caching is disabled and a noop runtime is installed.

Solutions

  1. Configure a provider in astro.config: cache: { provider: '...' }, or use an adapter that ships a default cache provider.
  2. Guard every call with the enabled flag: if (Astro.cache.enabled) { ... } — the interface exposes it for exactly this check.
  3. In dev, treat caching as a no-op and skip invalidation logic, or verify behavior in a preview/build run where the provider is active.

Example fix

// before
await Astro.cache.invalidate({ tags: ['posts'] });

// after
if (Astro.cache.enabled) {
  await Astro.cache.invalidate({ tags: ['posts'] });
}
// and enable the feature in astro.config.mjs:
// export default defineConfig({ cache: { provider: '...' } });
Defensive patterns

Strategy: type-guard

Validate before calling

// Gate every cache interaction on the runtime flag
if (Astro.cache.enabled) {
  await Astro.cache.invalidate({ tags: ['posts'] });
} else {
  // dev or provider-less deploy: caching is a no-op here
}

Type guard

const canInvalidate = (cache: { enabled: boolean }): boolean => cache.enabled;

// usage
if (canInvalidate(Astro.cache)) await Astro.cache.invalidate({ tags });

Prevention

When it happens

Trigger: Calling Astro.cache.invalidate(...) or context.cache.invalidate(...) without cache: { provider: ... } in astro.config and without an adapter that injects a default provider; using any cache method during astro dev, where the noop runtime is active.

Common situations: Testing cache invalidation locally in dev; deploying with the static adapter (no cache provider) while the code assumes the node adapter's cache; enabling cache-related code paths before wiring the cache config.

Related errors


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

Appendix: source

Thrown at packages/astro/src/core/cache/runtime/cache.ts:103

	get tags(): string[] {
		return [...this.#tags];
	}

	/**
	 * Get the current cache options (read-only snapshot).
	 * Includes all accumulated options: maxAge, swr, tags, etag, lastModified.
	 */
	get options(): Readonly<CacheOptions> {
		return {
			...this.#options,
			tags: this.tags,
		};
	}

	async invalidate(input: InvalidateOptions | LiveDataEntry): Promise<void> {
		if (!this.#provider) {
			throw new AstroError(CacheNotEnabled);
		}
		let options: InvalidateOptions;
		if (isLiveDataEntry(input)) {
			options = { tags: input.cacheHint?.tags ?? [] };
		} else {
			options = input;
		}
		return this.#provider.invalidate(options);
	}

	/** @internal */
	[APPLY_HEADERS](response: Response, request: Request): void {
		if (this.#disabled) return;
		const finalOptions: CacheOptions = { ...this.#options, tags: this.tags };
		if (finalOptions.maxAge === undefined && !finalOptions.tags?.length) return;

		const headers =
			this.#provider?.setHeaders?.(finalOptions, request) ?? defaultSetHeaders(finalOptions);

View on GitHub (pinned to e294953aa8)