{"record":{"id":"4144b665689eea8f","repo":"withastro/astro","slug":"configured-image-service-is-not-a-local-service","errorCode":null,"errorMessage":"Configured image service is not a local service","messagePattern":"Configured image service is not a local service","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/assets/endpoint/generic.ts","lineNumber":19,"sourceCode":"// @ts-expect-error\nimport { imageConfig } from 'astro:assets';\nimport { isRemotePath } from '@astrojs/internal-helpers/path';\nimport { isRemoteAllowed } from '@astrojs/internal-helpers/remote';\nimport * as mime from 'mrmime';\nimport type { APIRoute } from '../../types/public/common.js';\nimport { getConfiguredImageService } from '../internal.js';\nimport { etag } from '../utils/etag.js';\nimport { loadImage } from './loadImage.js';\n\n/**\n * Endpoint used in dev and SSR to serve optimized images by the base image services\n */\nexport const GET: APIRoute = async ({ request, logger }) => {\n\ttry {\n\t\tconst imageService = await getConfiguredImageService();\n\n\t\tif (!('transform' in imageService)) {\n\t\t\tthrow new Error('Configured image service is not a local service');\n\t\t}\n\n\t\tconst url = new URL(request.url);\n\t\tconst transform = await imageService.parseURL(url, imageConfig, logger);\n\n\t\tif (!transform?.src) {\n\t\t\tthrow new Error('Incorrect transform returned by `parseURL`');\n\t\t}\n\n\t\tlet inputBuffer: ArrayBuffer | undefined = undefined;\n\n\t\tconst isRemoteImage = isRemotePath(transform.src);\n\n\t\tif (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) {\n\t\t\treturn new Response('Forbidden', { status: 403 });\n\t\t}\n\n\t\tconst sourceUrl = new URL(transform.src, url.origin);","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/withastro/astro/blob/e294953aa8aadd98d5be92e60a03037b05dbdfd4/packages/astro/src/assets/endpoint/generic.ts#L1-L37","documentation":"Thrown by the `/_image` endpoint that serves optimized images in dev and SSR (packages/astro/src/assets/endpoint/generic.ts:19). The endpoint gets the configured image service via `getConfiguredImageService()` and requires it to implement `transform` — i.e. a local/base service that actually processes image bytes. Services that only rewrite URLs (passthrough, CDN-style, adapter-injected) cannot serve this endpoint, so any request to it fails and the endpoint returns a 500 after logging 'Could not process image request:'.","triggerScenarios":"A request reaches `/_image?...` while `image.service.entrypoint` resolves to a service without a `transform` method: `astro/assets/services/passthrough`, a custom/third-party service that only implements `getUrl`, or an adapter that swaps in a non-local service at runtime.","commonSituations":"Setting the passthrough service to avoid installing sharp, then rendering `<Image>`/`getImage()` which generate `/_image` URLs; edge adapters defaulting to a passthrough service; upgrading a project where the image service config changed.","solutions":["In `astro.config.mjs`, set `image.service.entrypoint` to a local service that implements `transform` (e.g. `astro/assets/services/sharp`, the default) and restart the dev server.","If you intentionally use a CDN-style service (only `getUrl`), keep it for production but point dev/SSR at a local service via environment-specific config so the `/_image` endpoint works.","Check whether an adapter or integration is overriding `image.service` with a passthrough service before your config takes effect.","If you never want optimization, import images directly (plain `<img>` with an ESM-imported URL) so `/_image` is never called."],"exampleFix":"// astro.config.mjs — before\nimage: { service: { entrypoint: 'astro/assets/services/passthrough' } }\n\n// astro.config.mjs — after\nimage: { service: { entrypoint: 'astro/assets/services/sharp' } }","handlingStrategy":"validation","validationCode":"// astro.config.mjs — fail fast at config load if the service cannot transform\nconst serviceEntrypoint = 'astro/assets/services/sharp';\nif (process.env.NODE_ENV === 'development' && !/sharp|base/.test(serviceEntrypoint)) {\n  throw new Error('Dev requires a local image service (implements transform), got: ' + serviceEntrypoint);\n}","typeGuard":"// For custom image service authors: guarantee a local service shape\nimport { baseService } from 'astro/assets/services/base';\n\nexport function isLocalService(service: unknown): service is typeof baseService {\n  return typeof service === 'object' && service !== null && 'transform' in service;\n}\n\n// build custom services on top of baseService so transform/parseURL always exist\nexport const service = { ...baseService /* , overrides */ };","tryCatchPattern":null,"preventionTips":["Treat the passthrough service as production-only; never run dev/SSR image URLs against it.","Assert the resolved image config after adapter setup (log it once in a config hook) so silent overrides surface early.","Smoke-test one `<Image>` render in dev before pushing config changes."],"tags":["images","image-service","configuration","dev-server","ssr"],"backgroundTag":"image-service-misconfigured","analyzedSha":"e294953aa8aadd98d5be92e60a03037b05dbdfd4","analyzedAt":"2026-08-18T18:48:03.901Z","contentChangedAt":"2026-08-18T18:48:03.901Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}