# Errors

Source: https://docx-editor.portone.io/docs/core/errors

The stable codes a refused import or export carries, and how to ask before writing.

`DocxImportError` and `DocxExportError` carry a stable `code`; their messages may be reworded.
Both classes are exported from the root entry as well.

```ts
import { DocxImportError, importDocx } from "@portone/docx-editor/core";

try {
  importDocx(source);
} catch (error) {
  if (error instanceof DocxImportError) {
    console.error(error.code);
  }
  throw error;
}
```

## Import codes [#import-codes]

| Code                      | Why the document was refused                                                                                                                                     |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `no-xml-parser`           | No `xmlParser` was passed and there is no `DOMParser` global. See [Server environments](https://docx-editor.portone.io/docs/core/server-environments.md).                                         |
| `not-a-docx`              | The bytes are not a readable zip container, or its entries are not a DOCX package's.                                                                             |
| `too-large`               | The package inflates beyond the [import limits](https://docx-editor.portone.io/docs/core.md#import-limits).                                                                                       |
| `missing-part`            | The package has no main document part.                                                                                                                           |
| `missing-body`            | The main part has no body.                                                                                                                                       |
| `malformed-xml`           | The XML cannot be parsed, declares a DTD, or is inconsistent.                                                                                                    |
| `unsupported-conformance` | The package is ECMA-376 Strict; the editor reads and writes Transitional.                                                                                        |
| `unsupported-content`     | The document holds markup that could not be written back unchanged, including a main part whose root does not bind the `w` prefix to the Transitional namespace. |

`not-a-docx` and `too-large` are usually the person's to fix; the others describe the file.

## Checking before export [#checking-before-export]

`exportProblems(doc, session)` reports the reasons `exportDocx` would refuse the document, in the
order the writer would raise them:

```ts
import { exportDocx, exportProblems } from "@portone/docx-editor/core";

const problems = exportProblems(doc, session);
if (problems.length > 0) {
  console.warn(problems[0].code, problems[0].reason.kind);
} else {
  const bytes = exportDocx(doc, session);
}
```

Each problem carries the `code` and `message` the `DocxExportError` would carry, a `reason` saying
which situation was met, and `pos` when the problem stands at a place in the document.
The `message` is an English one-liner for a developer and may be reworded; switch on `code` and
`reason.kind` instead.

| `reason.kind`                     | `code`                  | What it names                                                                                             |
| --------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- |
| `unreadable-preserved-xml`        | `malformed-xml`         | Preserved markup holding a bookmark that could not be parsed.                                             |
| `unnamed-bookmark`                | `malformed-xml`         | A bookmark `marker`, `"start"` or `"end"`, carrying no id to pair it by.                                  |
| `unmatched-bookmark`              | `malformed-xml`         | The bookmark `id` whose partner is gone, and which `marker` is left standing.                             |
| `repeated-bookmark-start`         | `malformed-xml`         | A bookmark `id` started twice.                                                                            |
| `unwritable-part-root`            | `malformed-xml`         | The `part` whose root element the writer cannot rewrite around.                                           |
| `conflicting-part-prefix`         | `unsupported-content`   | The `path` of a part a change is written into, and the `prefix` its root binds to a namespace of its own. |
| `vertical-merge-past-table`       | `invalid-table`         | A vertical merge reaching past the last row of its table.                                                 |
| `lost-preserved-xml`              | `lost-original`         | The `node` written from its original XML alone, which it no longer holds.                                 |
| `preserved-from-another-document` | `lost-original`         | A placeholder pasted in from another open document, and the `sessionId` it was opened in.                 |
| `duplicate-preserved-block`       | `unsupported-content`   | The `node` standing in two places.                                                                        |
| `undefined-list`                  | `unsupported-content`   | The `numId` of a list nothing defines.                                                                    |
| `unwritten-story-change`          | `unsupported-content`   | The `story` whose `change` - `"added"`, `"edited"` or `"removed"` - nothing writes back.                  |
| `story-id-not-a-number`           | `unsupported-content`   | The `story` added under an id no entry can be identified by.                                              |
| `missing-content-types`           | `missing-content-types` | The `part` the writer would add to a package with no content types part to declare it in.                 |

Every reason but `unwritable-part-root`, `conflicting-part-prefix` and `missing-content-types`
carries a `story`: the side story the content stands in, or `null` for the body. A `story` has a
`kind` (`comment`, `footnote`, `endnote`, `header` or `footer`) and an `id`: a note's number, a
comment's id, or a header or footer part's path. A `part` is `media`, `numbering`, `comments`,
`commentsExtended`, `people`, `commentsIds`, `commentsExtensible`, `footnotes` or `endnotes`. A
`path` is where the part sits in the package.

That is enough to say what a person has to put back:

```ts
import type { ExportProblem } from "@portone/docx-editor/core";

function whatToRevert({ reason }: ExportProblem): string {
  switch (reason.kind) {
    case "unwritten-story-change":
      return `Revert ${reason.story.kind} ${reason.story.id} to save this file.`;
    case "duplicate-preserved-block":
      return "A block was copied. Delete the copy to save this file.";
    case "undefined-list":
      return "Remove the list from this paragraph to save this file.";
    default:
      return "This document cannot be saved as it stands.";
  }
}
```

`pos` is a position in the body: the block or marker itself, or, for a footnote or an endnote, the
first reference to that note, since the note's text stands nowhere in the body. It is absent for a
problem of the package, for content of a header, footer or comment story, and for a note nothing
refers to; `reason.story` then says which story to open.

`exportDocx` throws the first entry of the same list, and the `DocxExportError` carries that entry
as `problem`, so a `catch` reads the same `reason` and `pos` without asking again.
An empty list does not guarantee that writing will succeed: a `DocxExportError` the list did not
predict is thrown under the same codes with no `problem`, for a main part that binds the
relationship prefix `r` to another namespace while a link is written (`unsupported-content`), for a
plugin that wrote an invalid node or attr, or for a bug in the editor.
Both functions need an XML parser, and are refused with the import code `no-xml-parser` without
one.
An export failure leaves the editor state untouched.
Inside an editor, [`canExport`](https://docx-editor.portone.io/docs/editor-api.md#export-controls) asks the same question of the
state.

## In the React component [#in-the-react-component]

When import fails, the component renders `renderImportError` or its built-in panel in place of the
editor; only `DocxImportError` is rendered, and anything else is thrown for an error boundary.
`downloadDocx` returns `blocked` with the problems above rather than throwing, while `exportBytes`
throws `DocxExportError`; a refusal the list did not predict is thrown by both.