Appearance
Fractional Order Serialization Fix (2026-05-21)
Background
PR #1859 (Block Grouping) rewrote block ordering from integer-based to fractional indexing. The fractional-indexing npm package generates string keys (like "a0", "a1", "aG") that sort lexicographically. This avoids renumbering every sibling on each reorder -- only the moved block gets a new key.
The backend validates order as schema.number() (see AB/start/validators/creative_validation.ts). A utility function serializeOrdersForSave() was created to convert string keys back to sequential integers (0, 1, 2, ...) before sending to the API.
The Bug
serializeOrdersForSave() was only called in updateChatbot() (normal save). It was missing from all other code paths that send creativeBlocks to the backend:
- First-time creative creation
- Mass format creation (creating multiple sizes at once)
- Adding formats to an existing creative
- Wizard-based creative editing and format conversion
This caused backend validation errors (Validation error: creative validation failed on creativeSettings.creativeBlocks.*.order) when creating new formats.
The Data Flow
Creative loaded from backend (integers: 0, 1, 2)
|
v
migrateNumericOrders() converts to strings ("a0", "a1", "a2")
|
v
In-memory state uses fractional strings for ordering
|
v
serializeOrdersForSave() converts back to integers before API call
|
v
Backend receives integers, validation passesFiles Changed
src/mixins/createCreative.ts
Central mixin used by the Creative Wizard and Templatizer for creating creatives. Two methods send creativeBlocks to the backend:
createNewCreative()-- creates a standalone creative. Constructs acreativeBlobpayload with blocks, properties, and delivery, then callschatbotService.createCreative(). Used when creating a single-format creative.createMassFormatCreative()-- creates a parent creative plus child creatives for different sizes. UsesgetCreativeBlob()to build the payload for each format. Called when the user selects multiple formats in the wizard. This was the primary trigger for the reported bug.
Both methods now wrap creativeBlocks with serializeOrdersForSave() before including in the payload.
src/pages/Chatbots/ChatbotBuilder.vue
The main builder page component. Has two save methods:
updateChatbot()-- already hadserializeOrdersForSave()(working correctly)createChatbot()-- first-time save when a new creative is created from the builder (not the wizard). Was missing the serialization. Now wrapsthis.creativeBlockswithserializeOrdersForSave().
src/components/CreativeWizard/wizardSavingLogic.ts
Mixin that handles all wizard save flows. Multiple methods construct payloads with creativeBlocks:
createAddedCreatives()-- adds new formats to an existing master creative. SendsmasterData.creativeSettings.creativeBlocksin each child payload.completeEditWizard()-- saves edits to a child creative's format. Sendsthis.creativeBlocksas part of the updated child creative blob.completeStandaloneEditWizard()-- converts a standalone creative to multi-format. Sendsthis.creativeBlocksfor both the new child creatives and the new master creative.
All three now serialize blocks before including in payloads.
src/utils/orderUtils.ts (not changed, for reference)
Contains the serialization utility:
serializeOrdersForSave(blocks)-- sorts blocks by their fractional string keys usingcompareOrder(), then assigns sequential integers (0, 1, 2, ...). Handles sub-blocks recursively (form inputs, slider templates, conversation children). Also nests group children back inside their parent group blocks for the backend/engine format.migrateNumericOrders(blocks)-- the inverse: converts integer orders to fractional strings on creative load. Runs insetCreativeBlocksmutation.
Related: Engine Animation Bug (CE)
A separate issue was reported: TypeError: e is not iterable in emitAnimationTriggers. This is unrelated to the serialization fix.
- Cause:
emitAnimationTriggers(components)was called withundefinedwhen a flow step had no components. AddedArray.isArray(components)guard in CE 6.9.0 (commitd06fb4e39). - Why it appears: Creatives built before CE 6.9.0 have the old engine code baked in. A rebuild bakes in the new code.
- Impact: Harmless -- creatives render correctly, but AdValidation tools flag the TypeError.
- Additional fix on theming branch:
655123f34guardsnextCompSetinaddComponentsToFlowto preventundefinedfrom reachingemitAnimationTriggersin the first place.