BookStackApp/BookStack · error · ZipImportException
Must not have a parent set for a Book import.
Error message
Must not have a parent set for a Book import.
What it means
ZipImportRunner::run() validates the relationship between the imported export model and the parent entity chosen in the UI/API. A ZipExportBook must be imported with no parent ($parent === null) because books are top-level entities; passing any parent throws ZipImportException 'Must not have a parent set for a Book import.'
Source
Thrown at app/Exports/ZipExports/ZipImportRunner.php:72
$reader = new ZipExportReader($zipPath);
$errors = (new ZipExportValidator($reader))->validate();
if ($errors) {
throw new ZipImportException([
trans('errors.import_validation_failed'),
...$errors,
]);
}
try {
$exportModel = $reader->decodeDataToExportModel();
} catch (ZipExportException $e) {
throw new ZipImportException([$e->getMessage()]);
}
// Validate parent type
if ($exportModel instanceof ZipExportBook && ($parent !== null)) {
throw new ZipImportException(["Must not have a parent set for a Book import."]);
} else if ($exportModel instanceof ZipExportChapter && !($parent instanceof Book)) {
throw new ZipImportException(["Parent book required for chapter import."]);
} else if ($exportModel instanceof ZipExportPage && !($parent instanceof Book || $parent instanceof Chapter)) {
throw new ZipImportException(["Parent book or chapter required for page import."]);
}
$this->ensurePermissionsPermitImport($exportModel, $parent);
if ($exportModel instanceof ZipExportBook) {
$entity = $this->importBook($exportModel, $reader);
} else if ($exportModel instanceof ZipExportChapter) {
$entity = $this->importChapter($exportModel, $parent, $reader);
} else {
$entity = $this->importPage($exportModel, $parent, $reader);
}
$this->references->replaceReferences();
View on GitHub (pinned to 18f8469a1c)
Solutions
- Omit parent_id (or pass null) when importing a book ZIP via the API.
- Re-select 'None' as the parent in the import form.
- Adjust automation scripts to branch on export type before setting the parent.
Example fix
// before curl -X POST .../import -d zip_id=5 -d parent_id=3 // after curl -X POST .../import -d zip_id=5
Defensive patterns
Strategy: validation
Validate before calling
$data = json_decode($zip->getFromName('data.json') ?: '', true);
$isBook = isset($data['book']);
if ($isBook && $parent !== null) {
throw new \InvalidArgumentException('Book imports must have no parent');
} Type guard
function parentIsValidFor(object $exportModel, $parent): bool {
if ($exportModel instanceof ZipExportBook) return $parent === null;
if ($exportModel instanceof ZipExportChapter) return $parent instanceof Book;
if ($exportModel instanceof ZipExportPage) return $parent instanceof Book || $parent instanceof Chapter;
return false;
} Try / catch
try {
$runner->run($import, $parent);
} catch (\BookStack\Exceptions\ZipImportException $e) {
// messages include parent-type mismatches; clear parent or fix type
} Prevention
- Detect export type from data.json before choosing the parent.
- API scripts: send parent_id only for chapter/page imports.
- Reset parent selection in the UI when switching import files.
When it happens
Trigger: Calling the import API / or the import form with parent_id/parent set while the ZIP contains a book export.
Common situations: Scripted API imports that always send a parent_id parameter; reusing a request payload built for a page or chapter import for a book ZIP; UI state retaining a previously selected parent.
Related errors
- Parent book required for chapter import.
- Parent book or chapter required for page import.
- errors.api_bad_authorization_format
- errors.api_user_token_not_found
- errors.import_zip_cant_decode_data
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/67addd12af4b29b5.
Report an issue: GitHub.