Skip to content

feat(fb_custom_audience): handle invalid fields and events#5023

Merged
shekhar-rudder merged 5 commits intodevelopfrom
int-5922-fb-audience-invalid-events
Mar 11, 2026
Merged

feat(fb_custom_audience): handle invalid fields and events#5023
shekhar-rudder merged 5 commits intodevelopfrom
int-5922-fb-audience-invalid-events

Conversation

@shekhar-rudder
Copy link
Member

Summary

  • Validate email fields using validator.isEmail; emit fb_custom_audience_invalid_email stats metric for invalid emails instead of silently passing them through
  • Validate country codes must be exactly 2 characters; emit fb_custom_audience_invalid_country_code stats metric for invalid codes
  • Throw InstrumentationError when all user properties for a record are null/invalid, replacing the previous silent stats counter (fb_custom_audience_event_having_all_null_field_values_for_a_user)
  • Pass workspaceId and destinationId through the call chain to enable proper metric attribution

Test plan

  • Verify events with valid emails are processed normally
  • Verify events with invalid emails (e.g. not-an-email) produce the fb_custom_audience_invalid_email stat and send an empty string in the payload
  • Verify events with invalid country codes (not 2 chars) produce the fb_custom_audience_invalid_country_code stat
  • Verify events where all user fields are null/invalid throw an InstrumentationError with a descriptive message
  • Run existing unit tests for fb_custom_audience

🤖 Generated with Claude Code

@shekhar-rudder shekhar-rudder requested a review from a team as a code owner March 6, 2026 06:38
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'auto_resolve_threads'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Summary by CodeRabbit

  • Bug Fixes

    • Improved error reporting for invalid data in Facebook Custom Audience exports, now detecting and reporting invalid email addresses, country codes, and incomplete records.
  • New Features

    • Added configuration option to control handling of invalid field values during exports.

Walkthrough

Adds workspaceId and destinationId propagation through record processing, a toggleable reject-invalid-fields feature, enhanced per-field validation for EMAIL/COUNTRY/PHONE, and replaces a metrics-only all-null-field path with throwing an InstrumentationError containing invalid/null-field details.

Changes

Cohort / File(s) Summary
Configuration & Feature Toggle
src/v0/destinations/fb_custom_audience/config.ts
Adds isRejectInvalidFieldsEnabled() that reads FB_CUSTOM_AUDIENCE_REJECT_INVALID_FIELDS and exports it.
Field Validation & Formatting
src/v0/destinations/fb_custom_audience/util.ts
Updates ensureApplicableFormat(...) signature to include workspaceId and destinationId; implements EMAIL and COUNTRY validation with conditional rejection (controlled by config), preserves PHONE normalization, threads workspace/destination IDs into validation, and replaces per-user all-null metrics path with throwing an InstrumentationError listing invalid/null fields.
Record Processing Pipeline
src/v0/destinations/fb_custom_audience/recordTransform.ts
Updates processRecord signature to accept workspaceId and destinationId, passes those through to formatting/validation, removes a stats-only all-null-field increment and instead surfaces an InstrumentationError, and ensures input metadata/destination ID are propagated.

Sequence Diagram(s)

sequenceDiagram
    participant Event as Input Event
    participant Proc as processRecord
    participant Prep as prepareDataField
    participant Val as ensureApplicableFormat
    participant Conf as config (isRejectInvalidFieldsEnabled)
    participant Out as Destination Output

    Event->>Proc: deliver record + metadata (workspaceId, destinationId)
    Proc->>Prep: map fields -> prepareDataField(field, workspaceId, destinationId)
    Prep->>Val: ensureApplicableFormat(property, value, workspaceId, destinationId)
    Val->>Conf: check isRejectInvalidFieldsEnabled()
    alt field valid or allowed
        Val-->>Prep: formatted/hashed value
    else field invalid and reject enabled
        Val-->>Prep: empty/removed value (or InstrumentationError for all-invalid user)
    end
    Prep->>Proc: assembled dataElement
    alt all fields invalid
        Proc-->>Event: throw InstrumentationError(invalid_fields...)
    else
        Proc->>Out: emit transformed record
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Reviewer suggestions for improvement

  • Ensure all call sites supplying processRecord are updated and validated to always pass non-empty workspaceId and destinationId.
  • Add unit tests for: EMAIL/COUNTRY valid vs invalid behavior with both states of FB_CUSTOM_AUDIENCE_REJECT_INVALID_FIELDS, PHONE normalization edge cases, and the InstrumentationError path when all user fields are invalid.
  • Confirm logging/monitoring captures the new InstrumentationError details (workspaceId, destinationId, invalid fields) for alerting and debugging.
  • Consider centralizing validation error keys/messages to keep metrics and logs consistent across destinations.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete; it provides a clear summary of changes but lacks crucial required sections from the template such as Linear task reference, explanations of behavior changes, dependencies, and utility modifications. Add the missing template sections: specify the related Linear task (INT-XXX), document behavior changes and reasons, note any new dependencies, and explain modifications to generic utilities. Complete the developer and reviewer checklists where applicable.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: adding validation for invalid email and country fields, and improving error handling for events with all null user properties in the fb_custom_audience destination.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch int-5922-fb-audience-invalid-events

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/v0/destinations/fb_custom_audience/recordTransform.ts (1)

104-116: ⚠️ Potential issue | 🟠 Major

Don’t let one bad record fail the whole chunk.

processRecord() now throws for an all-null/invalid record, and that exception will reject mapInBatches() for the entire recordArray. The result is batch-level data loss: one invalid input prevents every other valid record in the same chunk from being transformed. This needs per-record isolation here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/recordTransform.ts` around lines 104 -
116, processRecord() can throw for a bad record which will reject mapInBatches
and cause the whole recordArray to fail; wrap the per-record transformation in a
try/catch inside the mapInBatches callback (the block using processRecord within
the forEachInBatches over recordChunksArray) so a single thrown error is caught,
convert that failure into a per-record error metadata entry pushed into
metadata, and return a harmless sentinel (e.g., null) for that record; after
mapInBatches completes, filter out null/failed entries from data before
continuing so valid records in the chunk are preserved. Ensure you reference the
same identifiers: processRecord, mapInBatches, forEachInBatches,
recordChunksArray, and metadata when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/v0/destinations/fb_custom_audience/recordTransform.ts`:
- Around line 64-66: The current guard uses isHashRequired so
ensureApplicableFormat(...) is skipped when hashing is off, allowing invalid
EMAIL/COUNTRY values through; remove the isHashRequired check so the format
validation runs for raw/unhashed events as well (i.e., call
ensureApplicableFormat when disableFormat is false regardless of
isHashRequired). Update the conditional in recordTransform.ts (around
updatedProperty assignment using eachProperty, userProperty, workspaceId,
destinationId) and make the equivalent change in processRecord() to keep the
audiencelist and record paths in sync.

In `@src/v0/destinations/fb_custom_audience/util.ts`:
- Around line 240-246: The validation call to ensureApplicableFormat is
currently only executed when isHashRequired is true, so unhashed uploads bypass
email/country validation and metrics; change the gating logic to run
ensureApplicableFormat whenever disableFormat is false (i.e., remove dependency
on isHashRequired) so updatedProperty = ensureApplicableFormat(eachProperty,
userProperty, workspaceId, destinationId) executes for both hashed and unhashed
paths and the new stats/validation are emitted; keep any downstream hashing
logic tied to isHashRequired unchanged.

---

Outside diff comments:
In `@src/v0/destinations/fb_custom_audience/recordTransform.ts`:
- Around line 104-116: processRecord() can throw for a bad record which will
reject mapInBatches and cause the whole recordArray to fail; wrap the per-record
transformation in a try/catch inside the mapInBatches callback (the block using
processRecord within the forEachInBatches over recordChunksArray) so a single
thrown error is caught, convert that failure into a per-record error metadata
entry pushed into metadata, and return a harmless sentinel (e.g., null) for that
record; after mapInBatches completes, filter out null/failed entries from data
before continuing so valid records in the chunk are preserved. Ensure you
reference the same identifiers: processRecord, mapInBatches, forEachInBatches,
recordChunksArray, and metadata when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 12b99750-96cd-4b59-86a5-40ba689ae40f

📥 Commits

Reviewing files that changed from the base of the PR and between a551b35 and 94678b6.

📒 Files selected for processing (3)
  • src/v0/destinations/fb_custom_audience/recordTransform.ts
  • src/v0/destinations/fb_custom_audience/transform.ts
  • src/v0/destinations/fb_custom_audience/util.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/v0/destinations/fb_custom_audience/config.ts (1)

127-136: Well-documented feature flag implementation.

The function is clean, follows existing patterns in this file (like getMaxPayloadSize), and the documentation clearly explains the behavior and default.

One minor consideration: the comparison === 'true' is case-sensitive, so 'TRUE' or 'True' won't enable the feature. This is a common convention, but if you want to be more lenient:

💡 Optional: Case-insensitive check
 function isRejectInvalidFieldsEnabled(): boolean {
-  return process.env.FB_CUSTOM_AUDIENCE_REJECT_INVALID_FIELDS === 'true';
+  return process.env.FB_CUSTOM_AUDIENCE_REJECT_INVALID_FIELDS?.toLowerCase() === 'true';
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/config.ts` around lines 127 - 136, The
feature-flag check in isRejectInvalidFieldsEnabled is case-sensitive and will
miss values like "TRUE" or "True"; update isRejectInvalidFieldsEnabled to
perform a case-insensitive check of
process.env.FB_CUSTOM_AUDIENCE_REJECT_INVALID_FIELDS (e.g., normalize with
toLowerCase() or a case-insensitive comparison after verifying the env var is
defined) so that any variant of "true" enables the flag while preserving current
behavior for other values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/v0/destinations/fb_custom_audience/config.ts`:
- Around line 127-136: The feature-flag check in isRejectInvalidFieldsEnabled is
case-sensitive and will miss values like "TRUE" or "True"; update
isRejectInvalidFieldsEnabled to perform a case-insensitive check of
process.env.FB_CUSTOM_AUDIENCE_REJECT_INVALID_FIELDS (e.g., normalize with
toLowerCase() or a case-insensitive comparison after verifying the env var is
defined) so that any variant of "true" enables the flag while preserving current
behavior for other values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 01f0ebb9-2ebc-4a9d-997e-375ca9f2a33d

📥 Commits

Reviewing files that changed from the base of the PR and between 94678b6 and 7ba825f.

📒 Files selected for processing (3)
  • src/v0/destinations/fb_custom_audience/config.ts
  • src/v0/destinations/fb_custom_audience/recordTransform.ts
  • src/v0/destinations/fb_custom_audience/util.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/v0/destinations/fb_custom_audience/util.ts
  • src/v0/destinations/fb_custom_audience/recordTransform.ts

@shekhar-rudder shekhar-rudder force-pushed the int-5922-fb-audience-invalid-events branch from 689e084 to 3d692b6 Compare March 10, 2026 12:20
@shekhar-rudder shekhar-rudder requested review from a team as code owners March 10, 2026 12:20
@shekhar-rudder shekhar-rudder force-pushed the int-5922-fb-audience-invalid-events branch from 3d692b6 to 718a4d7 Compare March 10, 2026 12:22
@achettyiitr achettyiitr self-requested a review March 10, 2026 12:23
achettyiitr
achettyiitr previously approved these changes Mar 10, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
src/v0/destinations/fb_custom_audience/recordTransform.ts (1)

64-70: ⚠️ Potential issue | 🟠 Major

Keep record-field validation independent from hashing.

processRecord() still skips ensureApplicableFormat() whenever isHashRequired === false, so raw/unhashed record events continue to forward invalid EMAIL/COUNTRY values untouched and never emit the new stats. This guard should key only off disableFormat.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/recordTransform.ts` around lines 64 -
70, processRecord() currently only calls ensureApplicableFormat(...) when
isHashRequired is true, so validation (and associated stats) is skipped for
unhashed records; change the guard to run ensureApplicableFormat whenever
disableFormat is false (i.e., if (!disableFormat) { updatedProperty =
ensureApplicableFormat(eachProperty, userProperty, workspaceId, destinationId);
}) so formatting/validation and stat emission are independent of isHashRequired,
leaving hashing logic separate.
src/v0/destinations/fb_custom_audience/util.ts (1)

254-260: ⚠️ Potential issue | 🟠 Major

Run field validation even when hashing is disabled.

This still keeps ensureApplicableFormat() behind isHashRequired, so raw/unhashed audience uploads bypass the new EMAIL/COUNTRY validation and metrics. Formatting should depend only on disableFormat; keep the hashing decision in getUpdatedDataElement().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/util.ts` around lines 254 - 260, The
code currently calls ensureApplicableFormat() only when isHashRequired is true,
which skips EMAIL/COUNTRY validation and metrics for unhashed uploads; move the
formatting decision to depend solely on disableFormat by invoking
ensureApplicableFormat(eachProperty, userProperty, workspaceId, destinationId)
whenever disableFormat is false (regardless of isHashRequired) and keep the
hashing decision logic inside getUpdatedDataElement(); update the block that
sets updatedProperty so that isHashRequired controls only hashing in
getUpdatedDataElement(), while ensureApplicableFormat is executed whenever
!disableFormat to enforce validation/metrics for raw uploads.
🧹 Nitpick comments (1)
src/v0/destinations/fb_custom_audience/util.ts (1)

279-290: The aggregate null-event metric is now effectively dead code.

With the new throw here, a non-empty userUpdateList either flips nullEvent to false or exits before the counter below runs, so fb_custom_audience_event_having_all_null_field_values_for_all_users now only fires on empty input. If that metric still matters, emit it before throwing; otherwise it should be removed to avoid stale telemetry.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/util.ts` around lines 279 - 290, The
stats.increment call for the metric
fb_custom_audience_event_having_all_null_field_values_for_all_users is now
unreachable when InstrumentationError is thrown; decide whether you want the
metric to fire for cases where every user's fields are null or to treat it as
dead telemetry. If you want the metric preserved, move the stats.increment(...)
invocation so it executes before the throw inside the userUpdateList handling
(ensure it still receives destinationId), otherwise remove the
stats.increment(...) line and any references to that metric to avoid sending
stale telemetry; update any tests or callers that rely on
nullEvent/InstrumentationError behavior accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/v0/destinations/fb_custom_audience/recordTransform.ts`:
- Around line 64-70: processRecord() currently only calls
ensureApplicableFormat(...) when isHashRequired is true, so validation (and
associated stats) is skipped for unhashed records; change the guard to run
ensureApplicableFormat whenever disableFormat is false (i.e., if
(!disableFormat) { updatedProperty = ensureApplicableFormat(eachProperty,
userProperty, workspaceId, destinationId); }) so formatting/validation and stat
emission are independent of isHashRequired, leaving hashing logic separate.

In `@src/v0/destinations/fb_custom_audience/util.ts`:
- Around line 254-260: The code currently calls ensureApplicableFormat() only
when isHashRequired is true, which skips EMAIL/COUNTRY validation and metrics
for unhashed uploads; move the formatting decision to depend solely on
disableFormat by invoking ensureApplicableFormat(eachProperty, userProperty,
workspaceId, destinationId) whenever disableFormat is false (regardless of
isHashRequired) and keep the hashing decision logic inside
getUpdatedDataElement(); update the block that sets updatedProperty so that
isHashRequired controls only hashing in getUpdatedDataElement(), while
ensureApplicableFormat is executed whenever !disableFormat to enforce
validation/metrics for raw uploads.

---

Nitpick comments:
In `@src/v0/destinations/fb_custom_audience/util.ts`:
- Around line 279-290: The stats.increment call for the metric
fb_custom_audience_event_having_all_null_field_values_for_all_users is now
unreachable when InstrumentationError is thrown; decide whether you want the
metric to fire for cases where every user's fields are null or to treat it as
dead telemetry. If you want the metric preserved, move the stats.increment(...)
invocation so it executes before the throw inside the userUpdateList handling
(ensure it still receives destinationId), otherwise remove the
stats.increment(...) line and any references to that metric to avoid sending
stale telemetry; update any tests or callers that rely on
nullEvent/InstrumentationError behavior accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 82c63e4b-efad-4285-98d2-71c420615059

📥 Commits

Reviewing files that changed from the base of the PR and between 7ba825f and 689e084.

📒 Files selected for processing (3)
  • src/v0/destinations/fb_custom_audience/config.ts
  • src/v0/destinations/fb_custom_audience/recordTransform.ts
  • src/v0/destinations/fb_custom_audience/util.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/v0/destinations/fb_custom_audience/config.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/v0/destinations/fb_custom_audience/util.ts (2)

278-291: ⚠️ Potential issue | 🟡 Minor

Dead code: nullEvent stat increment is unreachable.

The stats increment at lines 287-291 will never execute. Here's why:

  1. nullEvent starts as true (line 244)
  2. For each user, if any property produces a truthy value, nullEvent is set to false (line 274)
  3. If all properties for a user are null/invalid, nullUserData remains true, and we throw at line 279

Since throwing exits the function immediately, the loop can only complete successfully if at least one user had at least one valid property—meaning nullEvent was set to false. Therefore, the condition if (nullEvent) at line 287 can never be true after the loop completes.

🔧 Suggested fix: remove unreachable code
     data.push(dataElement);
   });

-  if (nullEvent) {
-    stats.increment('fb_custom_audience_event_having_all_null_field_values_for_all_users', {
-      destinationId,
-    });
-  }
-
   return data;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/util.ts` around lines 278 - 291, The
stats.increment call guarded by nullEvent is unreachable because nullUserData
throws before the function can complete when all user fields are null; remove
the dead code by deleting the nullEvent variable usage and the block calling
stats.increment('fb_custom_audience_event_having_all_null_field_values_for_all_users',
{ destinationId }) (and remove nullEvent if no longer used elsewhere) so only
reachable, meaningful stats remain in util.ts.

1-1: ⚠️ Potential issue | 🟡 Minor

Address Prettier formatting failure.

The CI pipeline indicates that this file fails the Prettier formatting check. Run prettier --write src/v0/destinations/fb_custom_audience/util.ts to resolve the formatting issues before merging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/util.ts` at line 1, Prettier is
failing on src/v0/destinations/fb_custom_audience/util.ts; run the formatter and
commit the changes by executing `prettier --write
src/v0/destinations/fb_custom_audience/util.ts` (or run your project's
lint/format script) and then add/commit the resulting file so the import line
("import lodash from 'lodash';") and the rest of util.ts conform to the
project's Prettier rules.
🧹 Nitpick comments (2)
src/v0/destinations/fb_custom_audience/recordTransform.ts (2)

64-80: Inconsistent parameter usage: function params vs direct extraction.

There's an inconsistency in how workspaceId and destinationId are sourced:

  • Lines 68-69: ensureApplicableFormat uses function parameters workspaceId, destinationId
  • Lines 78-79: getUpdatedDataElement extracts directly from record.metadata.workspaceId, record.destination.ID

While these values should be identical, the inconsistent access pattern reduces clarity and could cause subtle bugs if assumptions change. Consider using the function parameters consistently.

♻️ Suggested refactor for consistency
     dataElement = getUpdatedDataElement(
       dataElement,
       isHashRequired,
       eachProperty,
       updatedProperty,
-      record.metadata.workspaceId,
-      record.destination.ID,
+      workspaceId,
+      destinationId,
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/recordTransform.ts` around lines 64 -
80, The call site mixes sources for workspaceId/destinationId:
ensureApplicableFormat uses the function parameters workspaceId and
destinationId while getUpdatedDataElement pulls record.metadata.workspaceId and
record.destination.ID; make them consistent by passing the same function
parameters (workspaceId, destinationId) into getUpdatedDataElement instead of
reading from record, so both calls use the same canonical values and avoid
subtle divergence between ensureApplicableFormat and getUpdatedDataElement.

133-134: Optional chaining suggests workspaceId may be undefined.

The use of recordChunksArray[0]?.[0]?.metadata?.workspaceId indicates awareness that this value could be undefined. This workspaceId is then passed to batchingWithPayloadSize. If the function doesn't handle undefined gracefully, this could cause issues with the config retrieval at config.getMaxPayloadSize(workspaceId).

💡 Consider adding a fallback
-    const workspaceId = recordChunksArray[0]?.[0]?.metadata?.workspaceId;
+    const workspaceId = recordChunksArray[0]?.[0]?.metadata?.workspaceId ?? '';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/recordTransform.ts` around lines 133 -
134, recordChunksArray[0]?.[0]?.metadata?.workspaceId may be undefined but we
pass it straight into batchingWithPayloadSize which likely calls
config.getMaxPayloadSize(workspaceId); ensure batchingWithPayloadSize receives a
safe workspaceId by providing a fallback or validating before call: derive
workspaceId from recordChunksArray (symbol: recordChunksArray) into a local
const workspaceId, if undefined replace with a sensible default (e.g., an
explicit default workspace id or undefined-safe placeholder) or
early-return/error, then call batchingWithPayloadSize(prepareFinalPayload,
workspaceId); alternatively update batchingWithPayloadSize to handle undefined
workspaceId and call config.getMaxPayloadSize only when workspaceId is present
(symbols: batchingWithPayloadSize, config.getMaxPayloadSize).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/v0/destinations/fb_custom_audience/util.ts`:
- Around line 278-291: The stats.increment call guarded by nullEvent is
unreachable because nullUserData throws before the function can complete when
all user fields are null; remove the dead code by deleting the nullEvent
variable usage and the block calling
stats.increment('fb_custom_audience_event_having_all_null_field_values_for_all_users',
{ destinationId }) (and remove nullEvent if no longer used elsewhere) so only
reachable, meaningful stats remain in util.ts.
- Line 1: Prettier is failing on src/v0/destinations/fb_custom_audience/util.ts;
run the formatter and commit the changes by executing `prettier --write
src/v0/destinations/fb_custom_audience/util.ts` (or run your project's
lint/format script) and then add/commit the resulting file so the import line
("import lodash from 'lodash';") and the rest of util.ts conform to the
project's Prettier rules.

---

Nitpick comments:
In `@src/v0/destinations/fb_custom_audience/recordTransform.ts`:
- Around line 64-80: The call site mixes sources for workspaceId/destinationId:
ensureApplicableFormat uses the function parameters workspaceId and
destinationId while getUpdatedDataElement pulls record.metadata.workspaceId and
record.destination.ID; make them consistent by passing the same function
parameters (workspaceId, destinationId) into getUpdatedDataElement instead of
reading from record, so both calls use the same canonical values and avoid
subtle divergence between ensureApplicableFormat and getUpdatedDataElement.
- Around line 133-134: recordChunksArray[0]?.[0]?.metadata?.workspaceId may be
undefined but we pass it straight into batchingWithPayloadSize which likely
calls config.getMaxPayloadSize(workspaceId); ensure batchingWithPayloadSize
receives a safe workspaceId by providing a fallback or validating before call:
derive workspaceId from recordChunksArray (symbol: recordChunksArray) into a
local const workspaceId, if undefined replace with a sensible default (e.g., an
explicit default workspace id or undefined-safe placeholder) or
early-return/error, then call batchingWithPayloadSize(prepareFinalPayload,
workspaceId); alternatively update batchingWithPayloadSize to handle undefined
workspaceId and call config.getMaxPayloadSize only when workspaceId is present
(symbols: batchingWithPayloadSize, config.getMaxPayloadSize).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2919ea9f-7ba0-40ff-9eea-af8966b62237

📥 Commits

Reviewing files that changed from the base of the PR and between 689e084 and 718a4d7.

📒 Files selected for processing (3)
  • src/v0/destinations/fb_custom_audience/config.ts
  • src/v0/destinations/fb_custom_audience/recordTransform.ts
  • src/v0/destinations/fb_custom_audience/util.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/v0/destinations/fb_custom_audience/config.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/v0/destinations/fb_custom_audience/util.ts (1)

260-266: ⚠️ Potential issue | 🟠 Major

Run field validation even when hashing is disabled.

This still skips ensureApplicableFormat() for raw/unhashed uploads, so invalid EMAIL/COUNTRY values pass through untouched and the new stats never fire on that path. The format-validation gate should be !disableFormat, independent of isHashRequired.

Suggested change
-      if (isHashRequired && !disableFormat) {
+      if (!disableFormat) {
         updatedProperty = ensureApplicableFormat(
           eachProperty,
           userProperty,
           workspaceId,
           destinationId,
         );
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/util.ts` around lines 260 - 266, The
current guard skips calling ensureApplicableFormat when hashing is disabled;
change the condition so format validation runs whenever disableFormat is false
regardless of isHashRequired. Update the branch around
isHashRequired/disableFormat to call ensureApplicableFormat(eachProperty,
userProperty, workspaceId, destinationId) whenever !disableFormat (and keep any
hashing logic separate), so updatedProperty is validated for raw/unhashed
uploads as well.
src/v0/destinations/fb_custom_audience/recordTransform.ts (1)

64-70: ⚠️ Potential issue | 🟠 Major

Apply the new EMAIL/COUNTRY validation on raw record uploads too.

processRecord() still ties ensureApplicableFormat() to isHashRequired, so the raw/unhashed record path never validates those fields and never emits the new invalid-field stats. This should use the same !disableFormat gate regardless of hashing.

Suggested change
-    if (isHashRequired && !disableFormat) {
+    if (!disableFormat) {
       updatedProperty = ensureApplicableFormat(
         eachProperty,
         userProperty,
         workspaceId,
         destinationId,
       );
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/v0/destinations/fb_custom_audience/recordTransform.ts` around lines 64 -
70, processRecord() currently only calls ensureApplicableFormat(...) when
isHashRequired is true, so raw/unhashed uploads skip EMAIL/COUNTRY validation
and never emit invalid-field stats; change the conditional so
ensureApplicableFormat is invoked whenever !disableFormat is true (independent
of isHashRequired) — locate the processRecord function and the block assigning
updatedProperty (where isHashRequired && !disableFormat is checked) and split or
replace that condition to call ensureApplicableFormat(updatedProperty
assignment) whenever !disableFormat, leaving any hashing logic (isHashRequired)
that follows intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/v0/destinations/fb_custom_audience/util.ts`:
- Around line 285-287: prepareDataField currently throws an InstrumentationError
inside the per-user loop when a single user has all null/invalid fields, which
aborts the entire batch; change the logic in prepareDataField to skip/ignore
individual invalid user rows (log or collect their indices if needed) rather
than throwing immediately, continue processing the rest of the batch, and only
throw an InstrumentationError after the loop if the final output array is empty
(i.e., all rows were invalid) — reference prepareDataField, InstrumentationError
and userSchema to locate and implement this behavior.

---

Duplicate comments:
In `@src/v0/destinations/fb_custom_audience/recordTransform.ts`:
- Around line 64-70: processRecord() currently only calls
ensureApplicableFormat(...) when isHashRequired is true, so raw/unhashed uploads
skip EMAIL/COUNTRY validation and never emit invalid-field stats; change the
conditional so ensureApplicableFormat is invoked whenever !disableFormat is true
(independent of isHashRequired) — locate the processRecord function and the
block assigning updatedProperty (where isHashRequired && !disableFormat is
checked) and split or replace that condition to call
ensureApplicableFormat(updatedProperty assignment) whenever !disableFormat,
leaving any hashing logic (isHashRequired) that follows intact.

In `@src/v0/destinations/fb_custom_audience/util.ts`:
- Around line 260-266: The current guard skips calling ensureApplicableFormat
when hashing is disabled; change the condition so format validation runs
whenever disableFormat is false regardless of isHashRequired. Update the branch
around isHashRequired/disableFormat to call ensureApplicableFormat(eachProperty,
userProperty, workspaceId, destinationId) whenever !disableFormat (and keep any
hashing logic separate), so updatedProperty is validated for raw/unhashed
uploads as well.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a41dd31d-f374-42b1-8df8-32169e8d9d94

📥 Commits

Reviewing files that changed from the base of the PR and between 718a4d7 and 1c78b25.

⛔ Files ignored due to path filters (2)
  • test/integrations/destinations/fb_custom_audience/processor/data.ts is excluded by !**/test/**
  • test/integrations/destinations/fb_custom_audience/router/data.ts is excluded by !**/test/**
📒 Files selected for processing (3)
  • src/v0/destinations/fb_custom_audience/config.ts
  • src/v0/destinations/fb_custom_audience/recordTransform.ts
  • src/v0/destinations/fb_custom_audience/util.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/v0/destinations/fb_custom_audience/config.ts

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 88.46154% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.27%. Comparing base (98f29cd) to head (1c78b25).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/v0/destinations/fb_custom_audience/util.ts 85.71% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #5023      +/-   ##
===========================================
- Coverage    92.27%   92.27%   -0.01%     
===========================================
  Files          657      657              
  Lines        35927    35941      +14     
  Branches      8455     8479      +24     
===========================================
+ Hits         33153    33163      +10     
+ Misses        2557     2540      -17     
- Partials       217      238      +21     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud
Copy link

@shekhar-rudder shekhar-rudder merged commit d4d9801 into develop Mar 11, 2026
33 of 34 checks passed
@shekhar-rudder shekhar-rudder deleted the int-5922-fb-audience-invalid-events branch March 11, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants