BookStackApp/BookStack · error · ZipImportException

Parent book required for chapter import.

Error message

Parent book required for chapter import.

What it means

ZipImportRunner::run() requires that a chapter export (ZipExportChapter) is imported with a parent that is a Book instance. If $parent is null or any other entity type, it throws ZipImportException 'Parent book required for chapter import.' Chapters can only live directly under books in BookStack's hierarchy.

Solutions

  1. Pass a valid Book id as parent_id in the import API request.
  2. Select a Book as the parent in the import form.
  3. Fetch the correct book id (e.g. via the search/API) before running the import.

Example fix

// before
POST /import {"zip_id": 7}
// after
POST /import {"zip_id": 7, "parent_id": 12} // 12 = a Book id
Defensive patterns

Strategy: validation

Validate before calling

$data = json_decode($zip->getFromName('data.json') ?: '', true);
if (isset($data['chapter']) && (!$parent instanceof \BookStack\Entities\Models\Book)) {
    throw new \InvalidArgumentException('Chapter imports require a Book parent');
}

Type guard

function chapterParentOk($parent): bool {
    return $parent instanceof \BookStack\Entities\Models\Book;
}

Try / catch

try {
    $runner->run($import, $parent);
} catch (\BookStack\Exceptions\ZipImportException $e) {
    // resolve a Book id and retry the import
}

Prevention

When it happens

Trigger: Importing a chapter ZIP with no parent selected, or with a Chapter/Page/Shelf passed as parent.

Common situations: API scripts forgetting parent_id for chapter imports; selecting the wrong parent in the import UI; automation that treats chapters like books.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/fd078da5ce166878. Report an issue: GitHub.

Appendix: source

Thrown at app/Exports/ZipExports/ZipImportRunner.php:74

        $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();

        $reader->close();
        $this->cleanup();

View on GitHub (pinned to 18f8469a1c)