vercel/next.js · error
Specified images.loader property (${images.loader}) cannot b
Error message
Specified images.loader property (${images.loader}) cannot be used with images.loaderFile property. Please set images.loader to "custom". What it means
`images.loaderFile` is only valid with `loader: 'default'` (where the file overrides the default) or `loader: 'custom'` (fully custom loader). The check at config.ts:789-794 throws when loaderFile is set but loader is a built-in named loader like 'cloudinary', because mixing a built-in loader with a custom file is contradictory and the framework cannot decide which to use.
Source
Thrown at packages/next/src/server/config.ts:791
images.path === imageConfigDefault.path &&
result.basePath &&
!pathHasPrefix(images.path, result.basePath)
) {
images.path = `${result.basePath}${images.path}`
}
// Append trailing slash for non-default loaders and when trailingSlash is set
if (
images.path &&
!images.path.endsWith('/') &&
(images.loader !== 'default' || result.trailingSlash)
) {
images.path += '/'
}
if (images.loaderFile) {
if (images.loader !== 'default' && images.loader !== 'custom') {
throw new Error(
`Specified images.loader property (${images.loader}) cannot be used with images.loaderFile property. Please set images.loader to "custom".`
)
}
const absolutePath = join(dir, images.loaderFile)
if (!existsSync(absolutePath)) {
throw new Error(
`Specified images.loaderFile does not exist at "${absolutePath}".`
)
}
images.loaderFile = absolutePath
}
}
warnCustomizedOption(
result,
'experimental.esmExternals',
true,
'experimental.esmExternals is not recommended to be modified as it may disrupt module resolution',View on GitHub (pinned to 0ae8c72462)
Solutions
- Set `loader: 'custom'` when supplying `loaderFile`: `images: { loader: 'custom', loaderFile: './loaders/my-loader.ts' }`.
- Or remove `loaderFile` if you want to keep the built-in named loader.
- Confirm the loaderFile exports a default function with the `({ src, width, quality }) => string` signature.
Example fix
// before
module.exports = { images: { loader: 'cloudinary', loaderFile: './my-loader.js' } }
// after
module.exports = { images: { loader: 'custom', loaderFile: './my-loader.js' } } Defensive patterns
Strategy: validation
Validate before calling
const { loader, loaderFile } = config.images ?? {};
if (loaderFile && loader && loader !== 'default' && loader !== 'custom') {
throw new Error('set images.loader to "custom" when using loaderFile');
} Type guard
function loaderFileCompatible(images: any): boolean {
return !images?.loaderFile || images?.loader === 'default' || images?.loader === 'custom' || !images?.loader;
} Prevention
- When introducing loaderFile, switch loader to 'custom' in the same edit.
- Lint for configs that have both a named loader and loaderFile.
When it happens
Trigger: Setting both `images: { loader: 'cloudinary', loaderFile: './my-loader.js' }`. The named loader would override the file, so the combination is rejected.
Common situations: Starting from a named loader config and adding a loaderFile to tweak behavior, instead of switching loader to 'custom'. Copying loaderFile from an example while leaving a previous loader value.
Related errors
- Specified images.loader property (${images.loader}) also req
- Specified images.loaderFile does not exist at "${absolutePat
- Specified images should be an object received ${typeof image
- Specified images.localPatterns should be an Array received $
- Specified images.remotePatterns should be an Array received
AI-assisted analysis of vercel/next.js@0ae8c72462 (2026-08-06).
Data as JSON: /api/errors/6b25247fcde400d2.
Report an issue: GitHub.