Skip to main content
Known Participant
September 25, 2026
Question

InDesign server crashes on delete operations when the story has images (anchored objects)

  • September 25, 2026
  • 2 replies
  • 39 views

Hi, InDesign Server crashes with an Access Violation during content deletion

We use InDesign ExtendScript with InDesign Server to automate production content updates. An Access Violation crash occurs when performing delete/remove operations on a story containing mixed content such as text, tables, and images.

The delta logic performs paragraph, table, row/column, and image/frame removal operations, along with XML metadata cleanup and rollback handling. The crash appears to occur during one of these removal operations and results in the InDesign Server process terminating, rather than an ExtendScript exception being thrown.


 

# Remove/Delete operations in `indesign-content-delta.jsx`

Surgical delta engine for InDesign Smart Update — here's every place it removes content, with the core code.

## 1. Paragraph delete (main story)

```js
// Verify identity before touching anything, then delegate to the shared primitive
if (currentText !== op.oldText) { /* normalized fallback check, else skip */ }
_delta_removeStoryParagraph(taggedElement, map.slots[idx].paraIndex, paragraph,
map.paragraphs.length, logger, "DELETE [" + tagName + "] idx=" + idx);
```

## 2. Shared paragraph-removal primitive

```js
function _delta_removeStoryParagraph(taggedElement, idx, paragraph, totalCount, logger, context) {
if (totalCount <= 1) {
// Can't empty a story — blank the text but keep a BOM anchor + inline/table markers
_delta_clearVisibleTextPreservingAnchors(taggedContent, logger, context, taggedElement);
return;
}

// Remove all characters of the paragraph INCLUDING its \r terminator
paragraph.characters.itemByRange(
paragraph.characters.firstItem(),
paragraph.characters.lastItem()
).remove();

// Last paragraph has no own \r — strip the previous paragraph's terminator too
if (idx === totalCount - 1 && idx > 0) {
var prevAfterDel = _delta_getContentParagraphs(taggedElement)[idx - 1];
if (prevAfterDel && prevAfterDel.characters.count() > 0) {
var prevLastCh = prevAfterDel.characters.lastItem();
if (prevLastCh.contents === "\r") { prevLastCh.remove(); }
}
}
}
```

## 3. Table-cell paragraph delete

```js
if (paraCount <= 1) {
// sole paragraph in cell — clear, never remove the cell's only paragraph
_delta_clearVisibleTextPreservingAnchors(paragraph, logger, context);
return true;
}

paragraph.characters.itemByRange(
paragraph.characters.firstItem(),
paragraph.characters.lastItem()
).remove();

if (idx === paraCount - 1 && idx > 0) {
var prevCellLastCh = cell.paragraphs[idx - 1].characters.lastItem();
if (prevCellLastCh.contents === "\r") { prevCellLastCh.remove(); }
}
```

## 4. Whole table delete (`table_delete` op)

```js
table.remove();

// Host paragraph outlives the table — remove it too if now empty
var host = afterParas[hostIdx];
if (hostTables === 0 && _delta_isBlankText(hostText) && host.characters.count() > 0) {
_delta_removeStoryParagraph(taggedElement, hostIdx, host, afterParas.length, logger, label);
}
```

## 5. Table row / column delete

```js
// Row
if (rowCount <= 1) return false; // refuse to remove the last row
table.rows[rowIndex].remove();

// Column
if (colCount <= 1) return false; // refuse to remove the last column
table.columns[colIndex].remove();
_delta_distributeColumnsEvenly(table, totalWidthBeforeRemoval, step, logger);
```

## 6. Merge cleanup (blank / duplicate paragraphs pulled in by `merge_cells`)

```js
// Strip blank trailing paragraphs absorbed by a merge
while (cell.paragraphs.count() > keepCount && cell.paragraphs.count() > 1) {
var last = cell.paragraphs[cell.paragraphs.count() - 1];
if (last.characters.count() > 0) {
last.characters.itemByRange(last.characters.firstItem(), last.characters.lastItem()).remove();
}
var prev = cell.paragraphs[cell.paragraphs.count() - 2];
if (prev && prev.characters.lastItem().contents === "\r") { prev.characters.lastItem().remove(); }
}
```

## 7. Image / graphic delete

```js
// Metadata first, then the container
_delta_clearImageMetadata(deleteMetadataTarget, logger, context);

if (removeContainingFrame && _delta_isValid(targetFrame) && targetFrame.remove) {
targetFrame.remove(); // paragraph/table-cell image: remove the whole anchored frame
} else {
targetGraphic.remove(); // direct DESIGN_GRAPHIC placeholder: remove just the graphic
}
```

## 8. Image replace/relocate (remove-then-place, with rollback)

```js
// Place the replacement FIRST, only remove the old one once the new one is valid
var newGraphic = _delta_placeImageInParagraph(taggedElement, tagName, op, localPath, logger);
if (!_delta_isValid(newGraphic) || _delta_sameGraphic(newGraphic, oldGraphic)) return null;

try {
oldGraphic.parent.remove();
} catch (removeOldFrameError) {
if (_delta_isValid(newGraphic.parent)) newGraphic.parent.remove(); // rollback
return null;
}
```

## 9. XML metadata attribute removal (accompanies every image delete)

```js
var attrs = ["href", "type", "docid", "docversionid", "wcrsmart_docversionid", "name"];
for (var i = 0; i < attrs.length; i++) {
var attr = taggedElement.xmlAttributes.itemByName(attrs[i]);
if (attr && attr.isValid) {
attr.remove(); // DOM removal
} else {
delete taggedElement["@" + attrs[i]]; // E4X fallback
}
}
```

## 10. Rollback on a failed `table_insert`

```js
function _delta_rollbackInsertedTable(taggedElement, table, hostParaIdx, targetCount, logger, label) {
if (_delta_isValid(table)) table.remove();
var host = _delta_getContentParagraphs(taggedElement)[hostParaIdx];
if (host && _delta_isBlankText(host.contents) && host.characters.count() > 0) {
_delta_removeStoryParagraph(taggedElement, hostParaIdx, host, /*...*/ logger, label + " rollback");
}
}
```

---

**Safety rules applied everywhere above:**
- Never empty a story to 0 paragraphs, or a table to 0 rows/columns — content is *cleared*, not removed, in that case.
- Any removal that shifts index space (paragraph delete, table_delete, row/column delete) aborts the rest of that tag/table's operations on failure, since every later index depends on the shift having happened.
- Image replace always places the new asset before removing the old one, with rollback if placement fails.

 

    2 replies

    leo.r
    Community Expert
    Community Expert
    September 25, 2026

    What versions of everything? I wonder if the same Generative AI options in preferences are responsible for this, too. Try to disable them (I assume they’re available in InDesign Server too).

    Harshika Verma
    Community Manager
    Community Manager
    September 25, 2026

    Hi adarsh_0848,


    Thank you for reaching out and sorry for the trouble. In addition to what our community expert has suggested, would you mind checking this post and let us know if that helps?


    We will try our best to help.


    Thanks,
    Harshika