Skip to content

fix(worker): add missing throttled property in step output#9901

Open
lqmanh wants to merge 3 commits intonovuhq:nextfrom
lqmanh:fix/missing-throttled-property
Open

fix(worker): add missing throttled property in step output#9901
lqmanh wants to merge 3 commits intonovuhq:nextfrom
lqmanh:fix/missing-throttled-property

Conversation

@lqmanh
Copy link

@lqmanh lqmanh commented Jan 23, 2026

What changed? Why was the change needed?

When using step.throttle via code-based workflows, this error is raised:

{
  "url": "...",
  "statusCode": 400,
  "message": "Workflow with id: `direct-message` has an invalid state. Step with id: `throttle-step` has invalid result. Please provide the correct step result.",
  "code": "ExecutionStateResultInvalidError",
  "data": [
    {
      "path": "",
      "message": "must have required property 'throttled'"
    }
  ],
  "cause": {
    "name": "HTTPError",
    "code": "ERR_NON_2XX_3XX_RESPONSE",
    "timings": {
      ...
    }
  }
}
Expand for optional sections

Related enterprise PR

Special notes for your reviewer

@netlify
Copy link

netlify bot commented Jan 23, 2026

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit 6c5bfa6
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/69923e848ba7a70008d454e1

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

Walkthrough

End-to-end throttle tests were changed to iterate over all completed and skipped jobs, validating each job's stepOutput fields (throttled, threshold, executionCount) across multiple scenarios instead of checking a single skipped job. The add-job use case was modified so that when a throttle check results in not skipping (shouldSkip === false), the job record is updated with stepOutput.throttled = false and the current executionCount and threshold before proceeding. No public APIs or exported signatures were changed.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding the missing 'throttled' property to step output in the worker, which directly addresses the validation error reported in the PR.
Description check ✅ Passed The description clearly relates to the changeset by explaining the validation error that occurs when using step.throttle and indicating that the fix adds the missing 'throttled' property.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into next

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


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: 1

🤖 Fix all issues with AI agents
In `@apps/api/src/app/events/e2e/throttle-events.e2e.ts`:
- Around line 100-106: The assertions assume DB returns completedThrottleJobs in
a deterministic order; instead make the check order-independent by collecting
execution counts from completedThrottleJobs (accessing
job.stepOutput?.executionCount) and asserting the resulting set/array contains
the expected values (e.g., {1,2,3} or includes each expected count) rather than
comparing to index + 1; update the loop/expectations around
completedThrottleJobs in throttle-events.e2e.ts to perform this set membership
check.
♻️ Duplicate comments (1)
apps/api/src/app/events/e2e/throttle-events.e2e.ts (1)

181-195: Same ordering issue as above for executionCount.

These assertions also assume deterministic ordering from the repository. Apply the same order-independent pattern as the earlier block.

Comment on lines 100 to 106
completedThrottleJobs.forEach((job, index) => {
expect(job.stepOutput).to.be.ok;
expect(job.stepOutput?.throttled).to.equal(false);
expect(job.stepOutput?.threshold).to.equal(3);
expect(job.stepOutput?.executionCount).to.equal(index + 1);
});

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid relying on DB order for executionCount assertions.

find() ordering isn’t guaranteed, so index + 1 can be flaky. Prefer order-independent checks.

🔧 Suggested change
-    completedThrottleJobs.forEach((job, index) => {
+    completedThrottleJobs.forEach((job) => {
       expect(job.stepOutput).to.be.ok;
       expect(job.stepOutput?.throttled).to.equal(false);
       expect(job.stepOutput?.threshold).to.equal(3);
-      expect(job.stepOutput?.executionCount).to.equal(index + 1);
     });
+
+    const executionCounts = completedThrottleJobs
+      .map((job) => job.stepOutput?.executionCount)
+      .filter((count): count is number => typeof count === 'number')
+      .sort((a, b) => a - b);
+    expect(executionCounts).to.deep.equal([1, 2]);
🤖 Prompt for AI Agents
In `@apps/api/src/app/events/e2e/throttle-events.e2e.ts` around lines 100 - 106,
The assertions assume DB returns completedThrottleJobs in a deterministic order;
instead make the check order-independent by collecting execution counts from
completedThrottleJobs (accessing job.stepOutput?.executionCount) and asserting
the resulting set/array contains the expected values (e.g., {1,2,3} or includes
each expected count) rather than comparing to index + 1; update the
loop/expectations around completedThrottleJobs in throttle-events.e2e.ts to
perform this set membership check.

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)
apps/worker/src/app/workflow/usecases/add-job/add-job.usecase.ts (1)

298-310: Good fix — correctly sets throttled: false on the non-skip path.

This resolves the reported validation error by ensuring stepOutput is populated regardless of the throttle outcome.

One minor note: handleThrottle has several early-return paths (lines 843, 850, 856, 863, 869) that return { shouldSkip: false } without executionCount or threshold. In those cases, undefined values will be written to the DB. This is unlikely to cause a runtime failure but may confuse downstream consumers expecting numeric values.

Optional: default to safe values
         } else {
           await this.jobRepository.updateOne(
             { _id: job._id, _environmentId: command.environmentId },
             {
               $set: {
                 stepOutput: {
                   throttled: false,
-                  executionCount: throttleResult.executionCount,
-                  threshold: throttleResult.threshold,
+                  executionCount: throttleResult.executionCount ?? 0,
+                  threshold: throttleResult.threshold ?? 0,
                 },
               },
             }
           );
         }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants