[data-reveal]{opacity:1!important;transform:none!important}
Results and Errors
Interface reference

Results and Errors

Overview

Every operation that can fail returns an OpResult. It is a discriminated union: ok: true carries the operation's data and the file changes it staged, ok: false carries a single BuilderError. Nothing throws for an expected failure — an unknown ref, a duplicate name, a config the engine cannot validate all come back as values.

Mutations are transactional. The engine applies the edit in memory, typechecks the project, and only writes when the typecheck passes; a failure rolls the transaction back and returns typecheck_failed, so a rejected operation leaves no partial edit on disk.

Branch on a Result

import { GetPageStructure } from "@antelopejs/interface-dms-builder";

const result = await GetPageStructure("/dashboard");
if (!result.ok) {
  console.error(result.error.code);
  return;
}

const { blocks, version } = result.data;

OpResult<T> is:

VariantFields
successok: true, data: T, changes: FileChange[], warnings?: OpWarning[]
failureok: false, error: BuilderError

OpResult defaults to OpResult<void>, which is what the operations that return nothing but a status use — ConfigurePage, RemoveBlock, RemoveField and the rest.

Reads that use the envelope — GetPageStructure, GetResourceStructure — return an empty changes array on success. The listing operations (ListPages, ListCategories, ListResources, ListQueryTemplates, GetCatalog) do not use the envelope at all: they return their value directly, and RefreshSourceIndex returns void.

Read the Staged Changes

changes is what the operation wrote, one entry per file.

FieldTypeDescription
pathstringThe file's absolute path.
kind"create" | "modify" | "delete"What happened to the file.
diffstringA textual diff of the change. Empty for a deletion.

A single operation usually touches more than one file — creating a page writes the page file and rewrites the barrel that exports it; creating a resource writes three files and rewrites a barrel.

import { CreateResource } from "@antelopejs/interface-dms-builder";

const result = await CreateResource({
  name: "product",
  fields: [{ name: "title", dataType: { $dataType: "string" } }],
});
if (result.ok) {
  for (const change of result.changes) {
    console.log(change.kind, change.path);
  }
}

Handle Warnings

warnings is present only when an otherwise successful operation has something non-fatal to report. It is optional: check for it rather than assuming an array.

CodeRaised when
index_clampedAn AddBlock or MoveBlock index fell outside the sibling range and was clamped into it.
datatype_fallbackA field's DataType is not one the builder maps, so it is handled as a string.
data_not_droppedDeleteResource removed the source but could not clear the resource's table data.
import { AddBlock } from "@antelopejs/interface-dms-builder";

const result = await AddBlock({
  page: "/revenue",
  name: "total",
  type: "KpiCard",
  config: { title: "Total" },
  index: 99,
});
if (result.ok) {
  for (const warning of result.warnings ?? []) {
    console.warn(warning.code, warning.message);
  }
}

Handle Errors

BuilderError is a union discriminated by code. Each variant carries exactly what you need to act on it.

CodePayloadMeaning
not_foundref: stringThe page, category, block, resource, field or query does not exist.
duplicate_namename: string, scope: DuplicateScopeThe name is already taken within that scope.
invalid_configissues: ValidationIssue[]The request's configuration did not validate.
opaque_targetpath: BlockPathThe target exists but the builder cannot rewrite it.
unsupporteddetail: stringThe operation is valid but out of scope for the engine, and detail says why.
referential_integrityblockedBy: string[]Something still depends on what you asked to delete.
staleref: string, currentVersion: stringThe file changed since the version you passed as expectedVersion.
typecheck_faileddiagnostics: TypecheckError[]The edit would not compile, and was rolled back.
import { AddBlock } from "@antelopejs/interface-dms-builder";

const result = await AddBlock({
  page: "/revenue",
  name: "total",
  type: "KpiCard",
  config: { title: "Total" },
});
if (!result.ok) {
  switch (result.error.code) {
    case "duplicate_name":
      console.error(`"${result.error.name}" is taken in ${result.error.scope}`);
      break;
    case "invalid_config":
      for (const issue of result.error.issues) {
        console.error(issue.pointer, issue.message);
      }
      break;
    case "typecheck_failed":
      for (const diagnostic of result.error.diagnostics) {
        console.error(`${diagnostic.file}:${diagnostic.line}`, diagnostic.message);
      }
      break;
    default:
      console.error(result.error.code);
  }
}

Duplicate Scopes

DuplicateScope names the namespace the collision happened in, which tells you what to rename:

ScopeThe name collided with
pageA top-level block on the same page, or another page's route.
siblingsAnother child of the same container block.
categoryAnother category.
resourceAnother resource, or another field on the same resource.
queryA member of the page controller, a route path already served by the page, or a member of the resource's model.

Validation Issues

ValidationIssue addresses a failure by JSON pointer into the request:

FieldTypeDescription
pointerstringA JSON pointer into the offending input, e.g. /where/0/op. Empty when the failure is not tied to one key.
messagestringWhat is wrong, and often what to call to find the valid values.

Typecheck Errors

TypecheckError is one TypeScript diagnostic from the in-memory typecheck gate:

FieldTypeDescription
filestringThe file the diagnostic points at.
linenumberThe line number.
messagestringThe diagnostic message.

Diagnostics are collected across the project, not just the file the operation edited, so a change that breaks a consumer elsewhere is caught before anything is written.

Make a Write Conditional

MutationOpts is the trailing options argument of version-checked mutations, not of every mutation.

OptionTypeDescription
expectedVersionstringThe version the consumer last read. A mismatch fails with stale.

The operations that edit something that already exists take it: ConfigurePage, DeletePage, AddBlock, ConfigureBlock, MoveBlock, RemoveBlock, DeleteResource, AddField, ConfigureField, RemoveField, AddQuery, ConfigureQuery and RemoveQuery. The three creations — CreatePage, CreateCategory and CreateResource — take no options argument, and neither do ConfigureCategory and DeleteCategory.

The version comes from the structure you read: PageStructure.version for a page operation, ResourceStructure.version for a resource one. Both are content hashes — of the page file, and of a resource's two generated files.

import {
  ConfigureBlock,
  GetPageStructure,
} from "@antelopejs/interface-dms-builder";

const read = await GetPageStructure("/revenue");
if (!read.ok) {
  return;
}

const write = await ConfigureBlock(
  "/revenue#total",
  { title: "Revenue" },
  { expectedVersion: read.data.version },
);
if (!write.ok && write.error.code === "stale") {
  // `write.error.currentVersion` is what the file hashes to now:
  // re-read the structure, re-decide, and retry.
}

Omitting expectedVersion writes unconditionally. Pass it whenever a human or another process may have touched the file between your read and your write.

Two operations extend MutationOpts with options of their own: ConfigureBlockOpts adds replace and meta, described in Page Operations, and DeleteResourceOpts adds keepData, described in Resources.