immich-app/immich · error · BadRequestException
Invalid import path
Error message
Invalid import path: ${path.message} What it means
This error is thrown by Immich's LibraryService.update when a library update request includes importPaths entries that fail validation. Before persisting the update, the service calls validate() and if any validated path has isValid=false, it aborts with a BadRequestException carrying the validator's per-path message. It protects the library from being saved with unreadable or disallowed import directories.
Solutions
- Check the per-path message in the error (it names the failing reason) and fix the path on disk so it exists, is a directory, and is readable by the server process
- Verify Docker volume mounts so the path is visible inside the server container
- Remove or correct the invalid entry in the update request's importPaths and resubmit
- Use a path that is not one of Immich's reserved internal directories (upload/, library/, encoded-video/, etc.)
Example fix
// before
await api.libraryApi.updateLibrary(id, { importPaths: ['/photos/old-folder'] }); // folder no longer exists
// after
await fs.mkdir('/photos/old-folder', { recursive: true }); // or point to an existing, mounted, allowed directory
await api.libraryApi.updateLibrary(id, { importPaths: ['/photos/old-folder'] }); Defensive patterns
Strategy: validation
Validate before calling
for (const p of importPaths) {
const stat = await fs.promises.stat(p).catch(() => null);
if (!stat || !stat.isDirectory()) throw new Error(`Import path is not a directory: ${p}`);
await fs.promises.access(p, fs.constants.R_OK); // readable
} Type guard
function isValidImportPath(p) {
return typeof p === 'string' && p.trim().length > 0 && require('path').isAbsolute(p);
} Try / catch
try {
await api.libraryApi.updateLibrary(id, { importPaths });
} catch (e) {
if (e?.response?.status === 400 && String(e.message).includes('Invalid import path')) {
console.warn('Rejected paths:', e.response.data?.message); // fix or drop offending paths and retry
} else throw e;
} Prevention
- Stat each path for existence and directory-ness before sending an update
- Ensure Docker volume mounts make the path visible inside the server container
- Avoid Immich-reserved internal directories as import paths
- Normalize path separators and avoid trailing slashes/relative paths
When it happens
Trigger: Calling the library update API (PATCH/PUT update) with dto.importPaths containing a path that the validator rejects (e.g. directory does not exist, is not readable, is a file rather than a directory, is a symlink, or points outside allowed boundaries such as an excluded/mount-invalid path).
Common situations: Admin adds an import folder via the UI/API after the folder was deleted or renamed on disk; Docker volume mounts changed so the container no longer sees the path; Windows vs Linux path separators or trailing-slash typos; path points to the upload/encoded-video/backup internal locations which are not allowed as import paths.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Skipping invalid import path
- assetIds, albumId, or userId is required
- At least two people are required for merging
- Cannot request to join your own cluster group
- error instanceof Error ? error.message : error
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/6e15f609699a0029.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/library.service.ts:351
return validation;
}
async validate(id: string, dto: ValidateLibraryDto): Promise<ValidateLibraryResponseDto> {
const importPaths = await Promise.all(
(dto.importPaths || []).map((importPath) => this.validateImportPath(importPath)),
);
return { importPaths };
}
async update(id: string, dto: UpdateLibraryDto): Promise<LibraryResponseDto> {
await this.findOrFail(id);
if (dto.importPaths) {
const validation = await this.validate(id, { importPaths: dto.importPaths });
if (validation.importPaths) {
for (const path of validation.importPaths) {
if (!path.isValid) {
throw new BadRequestException(`Invalid import path: ${path.message}`);
}
}
}
}
const library = await this.libraryRepository.update(id, dto);
return mapLibrary(library);
}
async delete(id: string) {
await this.findOrFail(id);
if (this.watchLibraries) {
await this.unwatch(id);
}
await this.libraryRepository.softDelete(id);
await this.jobRepository.queue({ name: JobName.LibraryDelete, data: { id } });View on GitHub (pinned to e55ac299a4)