Skip to content

fix: empty catch blocks, falsy JSON escape, and stale closure in workers - #42039

Closed
PedroHenrique0713 wants to merge 6 commits into
appsmithorg:releasefrom
PedroHenrique0713:fix/worker-error-handling
Closed

fix: empty catch blocks, falsy JSON escape, and stale closure in workers#42039
PedroHenrique0713 wants to merge 6 commits into
appsmithorg:releasefrom
PedroHenrique0713:fix/worker-error-handling

Conversation

@PedroHenrique0713

@PedroHenrique0713 PedroHenrique0713 commented Jul 23, 2026

Copy link
Copy Markdown

Summary

Three fixes in Appsmith worker/evaluation engine.

# File Bug
1 jsLibrary.ts Empty catch swallowed URL parsing errors silently
2 formEval.ts Empty catch swallowed dynamic form evaluation errors
3 evaluationSubstitution.ts JSON.parse('false') returned false, skipping escape for valid JSON strings that are falsy

Summary by CodeRabbit

  • Bug Fixes
    • Improved form and dynamic value handling when actions lack identifiers.
    • Fixed name matching for prefixes containing special characters.
    • Corrected archived action handling when JavaScript collection names change.
    • Improved URL validation and sanitization for missing or invalid values.
    • Prevented errors when scrolling to missing widgets or calculating visibility for zero-size elements.
    • Corrected action-difference path construction and dynamic string substitution.
  • Refactor
    • Updated array helpers to avoid unintended input mutations.
    • Clarified error handling and fallback behavior across evaluation utilities.

… lookup

fix(AppsmithUtils): escape regex special characters in getNextEntityName and
getDuplicateName. Entity name prefixes containing regex metacharacters (. $ * + etc.)
caused incorrect auto-increment numbering and potential name collisions.

fix(helpers): guard against division by zero in isElementVisibleInContainer.
When an element has zero dimensions, elementArea === 0 caused Infinity/NaN
comparisons that falsely reported the element as visible.

fix(helpers): add null guard to getWidgetElementToScroll.
Accessing widget.parentId without checking if canvasWidgets[widgetId]
exists caused TypeError on stale/deleted widget IDs.
fix(retryPromise): use reject() instead of Promise.reject() inside setTimeout.
  The setTimeout callback return value is ignored, so Promise.reject() created
  a dangling rejection that never reached the outer Promise's reject handler,
  causing the promise to hang forever when retries were exhausted.

fix(areArraysEqual): use element-wise comparison instead of join(",").
  Array elements containing commas caused false positives (e.g.,
  ["a,b"] and ["a","b"] incorrectly reported as equal).
fix(helpers): pushToArray no longer mutates input array in-place.
  Previously arr1.push(item) mutated the caller's array, violating
  immutability expectations in Redux state. Now creates a new array.

fix(getPathAndValueFromActionDiffObject): fix reduce accumulator path
  computation and early return. The reduce callback referenced the outer
  'path' variable instead of 'acc', causing path to grow incorrectly
  across multiple diff elements. The return statement was also inside
  the for loop, exiting prematurely instead of checking all elements.
fix(JSPaneUtils): add missing return to findIndex callback.
  Arrow function with curly braces returned undefined, so findIndex always
  returned -1, making the archived action lookup silently fail.

fix(TypeHelpers): add null guard to isURL before regex test.
  RegExp.prototype.test throws TypeError on null/undefined input from
  dynamic data sources.

fix(URLUtils): add null guard to sanitizeString.
  str.toLowerCase() throws TypeError on null/undefined from form fields.
  When action is not found, action?.id ?? '' produced an empty string '',
  which was used as key in state.evaluations.triggers[''], returning
  undefined and silently passing through extractConditionalOutput.

  Fix: return early with empty/default object when no action found.
fix(jsLibrary): replace empty catch with comment explaining fallback behavior.
  Empty catch silently swallowed URL parsing errors with no context.

fix(formEval): replace empty catch with comment explaining default fallback.
  Dynamic form evaluation errors were silently swallowed with no indication.

fix(evaluationSubstitution): decouple JSON detection from truthiness.
  JSON.parse('false') returned false, skipping escape for valid JSON
  string values that happen to be falsy ('false','0','null').
@PedroHenrique0713
PedroHenrique0713 requested a review from a team as a code owner July 23, 2026 20:10
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR updates client selectors, utilities, helpers, diff processing, and evaluation logic to handle missing identifiers, invalid inputs, regex metacharacters, immutable arrays, corrected predicates, and JSON parsing outcomes.

Changes

Client safeguards

Layer / File(s) Summary
Selector action guards
app/client/src/selectors/formSelectors.ts
Form selectors return typed empty objects when the resolved action lacks an id.
Utility matching and comparison fixes
app/client/src/utils/AppsmithUtils.tsx
Entity-name prefixes are regex-escaped, terminal retries reject directly, and string arrays are compared element-wise.
Diff and collection handling
app/client/src/utils/JSPaneUtils.tsx, app/client/src/utils/getPathAndValueFromActionDiffObject.ts
Archived action lookup returns the matching id, while diff paths are built and returned through corrected control flow.
Input and helper safeguards
app/client/src/utils/TypeHelpers.ts, app/client/src/utils/URLUtils.ts, app/client/src/utils/helpers.tsx
URL, string, visibility, widget lookup, and array helpers handle invalid or empty inputs safely.
Evaluation substitution and error paths
app/client/src/workers/Evaluation/evaluationSubstitution.ts, app/client/src/workers/Evaluation/formEval.ts, app/client/src/workers/Evaluation/handlers/jsLibrary.ts
Template substitution validates JSON before escaping, and catch blocks document existing fallback behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Poem

Regex stars now stay in line,
Empty ids yield objects fine.
Paths find their proper way,
Arrays leave old state at bay,
Safe helpers brighten review day.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description only gives a summary table and misses required sections like Fixes, Automation, Cypress results, and Communication. Add the repository template sections, include the linked issue, fill the Automation/Cypress sections, and complete the Communication checkbox.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title reflects the worker/evaluation fixes, but it adds an unmentioned stale-closure claim and omits other major changes.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/client/src/utils/AppsmithUtils.tsx (1)

438-443: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Sort both arrays once before comparing.

[...arr2].sort() is recomputed on every every iteration, causing unnecessary allocations and O(n²) behavior. Create sorted copies of both arrays once, then compare them.

Suggested refactor
 export function areArraysEqual(arr1: string[], arr2: string[]) {
   if (arr1.length !== arr2.length) return false;

-  return [...arr1].sort().every((val, i) => val === [...arr2].sort()[i]);
+  const sortedArr1 = [...arr1].sort();
+  const sortedArr2 = [...arr2].sort();
+  return sortedArr1.every((val, i) => val === sortedArr2[i]);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/client/src/utils/AppsmithUtils.tsx` around lines 438 - 443, Update
areArraysEqual to create sorted copies of both arr1 and arr2 once before the
comparison, then compare those cached arrays in every. Preserve the existing
length check and avoid sorting or allocating arr2 inside the iteration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/client/src/selectors/formSelectors.ts`:
- Around line 77-78: Update the missing-action branch in the selector containing
actionId to return a fully typed DynamicValues default with safe initialized
values, especially data as an empty array, instead of casting an empty object.
Preserve the existing consumer contract in SegmentedControl so
dynamicFetchedValues.data remains defined when no action ID is available.

In `@app/client/src/utils/JSPaneUtils.tsx`:
- Line 103: Format the findIndex predicate in the updateExisting flow using the
repository’s Prettier configuration so the declaration is wrapped to the
expected line length; do not change its behavior.

---

Nitpick comments:
In `@app/client/src/utils/AppsmithUtils.tsx`:
- Around line 438-443: Update areArraysEqual to create sorted copies of both
arr1 and arr2 once before the comparison, then compare those cached arrays in
every. Preserve the existing length check and avoid sorting or allocating arr2
inside the iteration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f2f07b28-c2af-49ca-93e5-fbae32cc4d26

📥 Commits

Reviewing files that changed from the base of the PR and between 583811d and c0861e8.

📒 Files selected for processing (10)
  • app/client/src/selectors/formSelectors.ts
  • app/client/src/utils/AppsmithUtils.tsx
  • app/client/src/utils/JSPaneUtils.tsx
  • app/client/src/utils/TypeHelpers.ts
  • app/client/src/utils/URLUtils.ts
  • app/client/src/utils/getPathAndValueFromActionDiffObject.ts
  • app/client/src/utils/helpers.tsx
  • app/client/src/workers/Evaluation/evaluationSubstitution.ts
  • app/client/src/workers/Evaluation/formEval.ts
  • app/client/src/workers/Evaluation/handlers/jsLibrary.ts

Comment on lines +77 to +78
const actionId = action?.id;
if (!actionId) return {} as DynamicValues;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return a safe DynamicValues shape for missing actions.

Casting {} to DynamicValues hides missing fields. app/client/src/components/formControls/SegmentedControl.tsx assigns dynamicFetchedValues.data directly to options, so this path changes the initialized [] to undefined when actionId is unavailable. Return typed defaults or coalesce these fields in the consumer.

As per coding guidelines, ensure TypeScript types are correct; this cast masks the required DynamicValues fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/client/src/selectors/formSelectors.ts` around lines 77 - 78, Update the
missing-action branch in the selector containing actionId to return a fully
typed DynamicValues default with safe initialized values, especially data as an
empty array, instead of casting an empty object. Preserve the existing consumer
contract in SegmentedControl so dynamicFetchedValues.data remains defined when
no action ID is available.

Source: Coding guidelines

const indexOfArchived = toBearchivedActions.findIndex((js) => {
js.id === updateExisting.id;
});
const indexOfArchived = toBearchivedActions.findIndex((js) => js.id === updateExisting.id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Format the changed predicate with Prettier.

Line 103 is overlong and should be wrapped to satisfy the repository’s formatting check.

As per coding guidelines, files under app/client/{src,cypress}/**/*.{ts,tsx,js} must pass Prettier via yarn run prettier.

Suggested formatting
-          const indexOfArchived = toBearchivedActions.findIndex((js) => js.id === updateExisting.id);
+          const indexOfArchived = toBearchivedActions.findIndex(
+            (js) => js.id === updateExisting.id,
+          );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const indexOfArchived = toBearchivedActions.findIndex((js) => js.id === updateExisting.id);
const indexOfArchived = toBearchivedActions.findIndex(
(js) => js.id === updateExisting.id,
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/client/src/utils/JSPaneUtils.tsx` at line 103, Format the findIndex
predicate in the updateExisting flow using the repository’s Prettier
configuration so the declaration is wrapped to the expected line length; do not
change its behavior.

Source: Coding guidelines

@PedroHenrique0713

Copy link
Copy Markdown
Author

Closing this one. I opened a stack of PRs in this repo today and, because each branch was cut from the previous one instead of from the base, they overlap: this PR carries the commits of the earlier ones as well. I am consolidating the work in #42033 and will resubmit the remaining fixes individually, on top of the base branch, once that one has been reviewed. Sorry for the noise.

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.

1 participant