fix(personalize): fix CT linking failure from empty-audience experiences #281
Conversation
…ces [DX-9469] pendingVariantAndVariantGrpForExperience was populated with ALL created experience UIDs regardless of whether valid variants were submitted to the API. Experiences with variants:[] never get variant groups from the Personalize backend, so the validation loop timed out and set importData=false, skipping attachCTsInExperience for every experience including valid ones. Fixed by: 1. Adding a versionMap guard in importExperienceVersions (returns boolean) so only experiences that actually submitted variants return true 2. Only adding UIDs to the pending set when importExperienceVersions returns true 3. Adding a null guard in attachCTsInExperience before accessing variantGroup to prevent a silent crash inside Promise.allSettled Note: Bug 2 (missing data-for-variant-entry.json) is not present in v2 — processCleanup always calls createEntryDataForVariantEntry regardless of environments. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 11 test cases covering importExperienceVersions returning false for missing file, Lytics-only audiences, and unmapped CS audiences - Add tests verifying Set-based pending list only includes experiences with valid variants submitted to Personalize - Add tests for validateVariantGroupAndVariantsCreated resolving immediately when pending list is empty - Add tests for attachCTsInExperience null guard skipping gracefully when variantGroup is missing - Fix mocha-root-hooks.js to guard loadChalk as optional (not exported in current cli-utilities version) - Fix importExperienceVersions to return false (not undefined) when versions file does not exist, for consistent boolean API Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
Consider reviewing these vulnerabilities when fixes become available. |
There was a problem hiding this comment.
Pull request overview
Fixes a Personalize experiences import edge case where experiences with no mappable audiences were still added to the variant polling queue, causing validation timeouts that flipped importData=false and skipped content-type (CT) attachment for the whole import.
Changes:
- Make
importExperienceVersionsreturn a boolean indicating whether variants were actually submitted, and only queue those experiences for variant/variant-group validation. - Add a null guard in
attachCTsInExperiencewhengetVariantGroupreturns no groups, logging a warning and skipping instead of throwing. - Add unit tests covering “no versions / no valid variants / Set-based pending list / empty pending validation / CT attach null-guard”, and harden mocha root hooks to treat
loadChalkas optional.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/contentstack-variants/src/import/experiences.ts | Only validates/polls for experiences that actually submitted variants; avoids CT-attach crash when variant group is missing. |
| packages/contentstack-variants/test/unit/import/experiences.test.ts | Adds unit coverage for boolean-return behavior, Set-based pending queue, empty-pending validation fast-path, and CT attach null-guard. |
| packages/contentstack-variants/test/helpers/mocha-root-hooks.js | Avoids failing the entire test suite when loadChalk is not exported by the installed @contentstack/cli-utilities. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Message was blaming Lytics audiences specifically but the branch fires for any mapping failure (CS or Lytics). Broadened to "after audience/event mapping". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
Consider reviewing these vulnerabilities when fixes become available. |
…orts [DX-9469] Both were made redundant by the Set-based pending list introduced in the DX-9469 fix. Leaving them risks build failures under noUnusedLocals or ESLint unused-import rules. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ Vulnerabilities Without Available Fixes (Informational Only)The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:
Consider reviewing these vulnerabilities when fixes become available. |
Summary
Fixes a bug in the Personalize import pipeline where experiences whose audience mappings are all empty (e.g. Lytics-only or unmapped CS audiences) were incorrectly added to the variant polling queue, causing the validation to time out and
importDatato be set tofalse— which in turn skipped content-type linking for all experiences in the import.Root Cause
importExperienceVersionsdid not return a boolean indicating whether variants were actually submitted to the Personalize backend. As a result, every experience's UID was unconditionally added topendingVariantAndVariantGrpForExperience, even those where all audience references were unmapped (so no API call was made). The polling loop then waited the full threshold timer (60 s) waiting for variant groups that would never be created, then setimportData = false, blocking CT attachment for all experiences.Additionally,
attachCTsInExperiencehad no null guard on thevariantGroupreturned bygetVariantGroup, which could throw if the backend returned an empty list.Changes
packages/contentstack-variants/src/import/experiences.tsimportExperienceVersionsnow returnsPromise<boolean>:falsewhen the versions file doesn't existfalsewhen the versionMap is all-undefined (every version had its variants stripped by audience lookup)trueonly afterhandleVersionUpdateOrCreateis called (i.e. something was actually submitted)import()— replacedvalues(cloneDeep(experiencesUidMapper))with aSet<string>that only collects UIDs whereimportExperienceVersionsreturnedtrue. Pending list is built withArray.from(experienceUidsWithVariants)after the loop.attachCTsInExperience— added null guard: ifgetVariantGroupreturns novariantGroup, log a warning and skip instead of throwing.packages/contentstack-variants/test/unit/import/experiences.test.tsAdded 11 new test cases covering the fix:
importExperienceVersionsfalse; Lytics-only audiences →false; unmapped CS audiences →false; valid ACTIVE →true; valid DRAFT-only →true[]; no-valid-versions experience not added; valid-versions experience addedvalidateVariantGroupAndVariantsCreatedtrueimmediately (no API calls)attachCTsInExperiencenull guardpackages/contentstack-variants/test/helpers/mocha-root-hooks.jsGuarded
loadChalkas optional — not exported in the current installed version of@contentstack/cli-utilities, which caused the entire test suite to fail before any test ran.Test Results
Impact
getVariantGroupreturns empty listvariantGroup.content_types