Typed error hierarchy —
MdError+ParseError+RenderError+ContractError.
Module mdErrors | Source packages/front/office/md/src/errors.js | Deps none | Worker-safe yes
Every error thrown by @awacloud/md extends MdError and carries a kebab-case code ('md/parse-not-string', 'md/render-invalid-root', …). Consumers can match on the code rather than the message.
Stable codes
| Code | Class | Thrown by | Meaning |
|---|---|---|---|
md/parse-not-string |
ContractError |
md.parse, blockParser.parse |
Non-string input argument |
md/use-bad-extension |
ContractError |
md.use |
Extension without install(md) |
md/render-invalid-root |
RenderError |
renderHtml, renderMarkdown, renderXml |
Missing or non-object AST root |
md/replace-node-no-parent |
ContractError |
replaceNode |
oldNode is detached |
md/wrap-node-no-parent |
ContractError |
wrapNode |
node is detached |
md/flatten-node-no-parent |
ContractError |
flattenNode |
node is detached |
md/limit-exceeded |
ContractError |
md.parse |
maxDepth / maxNodes / maxUrlLength exceeded |
md/parse-error |
ContractError |
blockCursor.incorporateLine (internal invariant) |
A block continue rule returned an illegal value |
md/document-bad-input |
ContractError |
mdHtmlDocument.renderFragment, mdHtmlDocument.build |
documents empty / entry without string path+source |
md/document-bad-option |
ContractError |
mdHtmlTheme.css, mdHtmlDocument.build |
Unknown theme, or an invalid mdHtmlDocument.build option |
Invalid input arguments (null/undefined objects passed to replaceNode/wrapNode/cloneNode) throw a native TypeError. That is a deliberate choice, aligned with the JavaScript convention for trivially-violable argument preconditions.
Resolve
mdErrors has no dependencies — its classes are declared inside the factory body (kept serialisable to a Worker via factory.toString()). Two consumption modes:
// 1. Via a ModuleRuntime — worker-safe, and the canonical mode: every
// other @awacloud/md module (`mdMod`, `blockParser`, `renderHtmlMod`,
// `renderMarkdownMod`, `renderXmlMod`, `mdAstManipulation`) receives
// this SAME resolved `mdErrors` instance as its first dependency, so
// `instanceof` checks agree package-wide:
import { runtime } from '@awacloud/fw';
import { fw_require, modules } from '@awacloud/md';
runtime.registerAll(fw_require);
runtime.registerAll(modules);
const { MdError, ParseError, RenderError, ContractError, isMdError } = runtime.resolve('mdErrors');
// 2. Direct factory call — an isolated instance, no runtime needed
// (`mdErrors` has zero dependencies of its own):
import { mdErrors } from '@awacloud/md';
const { MdError, ParseError, RenderError, ContractError, isMdError } = mdErrors.factory();
@awacloud/md's package root (src/main.js) is a strict descriptor manifest: it exports themodules/extras/bundle/fw_requirearrays plus every module descriptor by binding name (mdErrors,mdCommon, …) — it never materializes or re-exports the error classes themselves. There is noimport { MdError } from '@awacloud/md'; the classes only exist aftermdErrors.factory()runs, either directly (mode 2 above) or through aModuleRuntime(mode 1). A consumer that calls another module's factory manually (e.g.blockParser.factory(...)) must pass its ownmdErrors.factory()result as the first argument, to keep a consistent class identity between that module and the consumer.
API
| Class | Thrown when |
|---|---|
MdError |
Base — never thrown directly |
ParseError |
Malformed input (rare in Markdown: the spec accepts anything) |
RenderError |
Invalid AST encountered while rendering |
ContractError |
Contract violation (wrong type, missing field) |
isMdError(e) |
(e) => boolean |
Fields
| Field | Type | Description |
|---|---|---|
name |
string |
Class name ('ParseError', etc.) |
code |
string |
Kebab-case identifier ('md/parse-not-string') |
message |
string |
Human-readable message |
context |
object? |
Structured data (optional) |
cause |
Error? |
Parent error (optional) |
Examples
Typed catch
import { runtime } from '@awacloud/fw';
import { fw_require, modules } from '@awacloud/md';
runtime.registerAll(fw_require);
runtime.registerAll(modules);
const { MdError, ContractError } = runtime.resolve('mdErrors');
const { createMd } = runtime.resolve('md');
try {
createMd().parse(123);
} catch (e) {
if (e instanceof ContractError) console.log(e.code); // 'md/parse-not-string'
if (e instanceof MdError) console.log(e.name);
}
With context
const { RenderError } = runtime.resolve('mdErrors');
const node = { type: 'mystery', sourcepos: null };
try {
throw new RenderError('md/render-invalid-root', 'Unknown node type', {
context: { type: node.type, sourcepos: node.sourcepos }
});
} catch (e) {
e.code; // 'md/render-invalid-root'
e.context; // { type: 'mystery', sourcepos: null }
}
Notes
MdErroris never thrown directly — always a subclass.- The code follows the
'md/<kebab-case>'format, for consistency with@awacloud/fwerrors. - Renderers use
RenderErrorto signal a corrupted AST (a node missingliteral, an unknown type). ContractErroris reserved for API violations by the caller (typically a wrong argument type).tests/errors-doc-snapshot.test.jslocates the table above by splitting on the exact### Stable codesheading — renaming that heading requires updating the test's anchor string in the same change.
See also
md— the facade that throwsContractErroron non-string inputrender/html— throwsRenderError@awacloud/fwerrors — the framework's own error pattern