Skip to content

fix(vscode): validate pipe connections and handle invalid pipeline data gracefully#553

Closed
charliegillet wants to merge 1 commit into
rocketride-org:developfrom
charliegillet:bugfix/invalid-pipe-crash
Closed

fix(vscode): validate pipe connections and handle invalid pipeline data gracefully#553
charliegillet wants to merge 1 commit into
rocketride-org:developfrom
charliegillet:bugfix/invalid-pipe-crash

Conversation

@charliegillet
Copy link
Copy Markdown
Contributor

@charliegillet charliegillet commented Mar 31, 2026

Summary

  • Adds connection-level validation to the pipeline file parser so .pipe files with broken input/control references are rejected with clear, actionable error messages before reaching the canvas
  • Adds defensive guards in the graph edge builder to silently skip malformed connection entries, preventing React Flow from receiving edges with undefined source/target
  • Wraps the canvas loadData path and the webview JSON parse in try/catch so unexpected data shapes show an error overlay instead of crashing the UI

Type

Bug fix

Why this bug happens in this codebase

The pipeline file parser (PipelineFileParser.validatePipelineStructure) only validated that components have id and provider fields, but never checked the input and control connection arrays. When a .pipe file contained connections with a missing from field, a missing lane field, or a from value referencing a component ID that doesn't exist in the file, the data passed validation and was sent to the canvas. The graph builder (getEdgesFromNodes) then created React Flow edges with undefined source or target handles, which caused React Flow to throw an unrecoverable error, crashing the entire canvas UI with a cryptic "invalid pipe" message and no recovery path.

What changed

File Change
apps/vscode/src/shared/util/pipelineParser.ts Added validation of input[].from, input[].lane, and control[].from fields on each component, including cross-reference checks against the component ID set
packages/shared-ui/src/modules/flow/util/graph.ts getEdgesFromNodes now builds a node ID set and skips any connection entry where from is missing or references a non-existent node
apps/vscode/src/providers/views/PageEditor/PageEditor.tsx Wrapped JSON.parse in the 'update' message handler with try/catch, routing parse failures to the file-invalid error overlay
packages/shared-ui/src/modules/flow/context/FlowGraphContext.tsx Wrapped loadData in try/catch so any unexpected error clears the canvas instead of crashing it

Validation

  • Malformed .pipe file with missing from in input connection shows clear error in the editor overlay
  • .pipe file referencing a non-existent component ID shows specific error message naming the bad reference
  • Valid .pipe files continue to load and render normally
  • Canvas does not crash on unexpected data shapes — falls back to empty canvas with console error

How this could be extended

  • Add a "Repair pipeline" button to the error overlay that strips invalid connections and reloads
  • Surface connection validation warnings (not just errors) for connections that reference valid but disconnected components
  • Add schema-level validation (JSON Schema) for .pipe files as a pre-parse step
  • Integrate the same validation into the headless pipeline execution path so the engine rejects invalid pipes before attempting to run them

Closes #395

#Hack-with-bay-2

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Added error handling with detailed validation messages for data parsing in the page editor
    • Enhanced validation of pipeline component connections and reference integrity
    • Added protective error handling when loading project data to prevent canvas crashes
    • Improved validation of flow graph connections to filter and handle invalid references

…ta gracefully

Malformed .pipe files with missing or dangling connection references (input/control
entries with no "from" field or referencing non-existent components) caused the
canvas UI to crash with an unrecoverable "invalid pipe" error. This adds validation
at three layers: the pipeline parser now rejects files with broken connections and
shows actionable errors, the graph builder silently skips malformed edges instead of
creating undefined source/target pairs, and the canvas loadData is wrapped in
try/catch to prevent total UI crashes from unexpected data shapes.

Closes rocketride-org#395

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added module:vscode VS Code extension module:ui Chat UI and Dropper UI labels Mar 31, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

These changes add validation and error handling throughout the pipeline processing stack to prevent crashes from malformed data. They validate component connections, catch JSON parsing errors, sanitize node references, and provide clear error messages rather than crashing the UI.

Changes

Cohort / File(s) Summary
Pipeline Validation
apps/vscode/src/shared/util/pipelineParser.ts
Enhanced validatePipelineStructure to validate input and control connection arrays, checking for valid from references to existing component IDs and required fields like lane. Appends specific error messages for missing/invalid references.
Flow Graph Error Handling
packages/shared-ui/src/modules/flow/context/FlowGraphContext.tsx, packages/shared-ui/src/modules/flow/util/graph.ts
Wrapped canvas conversion pipeline in try/catch to prevent crashes; getEdgesFromNodes now precomputes node ID set and filters invalid connection references instead of creating malformed edges.
Message Parsing Safety
apps/vscode/src/providers/views/PageEditor/PageEditor.tsx
Added try/catch around JSON parsing of incoming messages; on parse failure, sets fileErrors with descriptive message instead of crashing; on success, clears errors and updates content.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

module:vscode, error-handling, validation, pipeline, bugfix

Suggested reviewers

  • jmaionchi
  • stepmikhaylov

Poem

🐰 A rabbit hops through pipelines with care,
Catching bad data floating through air,
With try and with catch and a nodeIdSet,
We validate connections—no crashes yet!
Clear error messages, no cryptic despair,

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding validation for pipe connections and implementing graceful error handling for invalid pipeline data.
Linked Issues check ✅ Passed The PR implements validation to prevent invalid pipe states, adds actionable error messages, and provides error recovery mechanisms (graceful degradation), directly addressing issue #395's acceptance criteria.
Out of Scope Changes check ✅ Passed All changes are directly related to validation and error handling for pipeline connections and data, with no unrelated modifications detected.
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 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 and usage tips.

Copy link
Copy Markdown
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

Caution

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

⚠️ Outside diff range comments (1)
packages/shared-ui/src/modules/flow/util/graph.ts (1)

256-277: ⚠️ Potential issue | 🔴 Critical

Malformed control/input payloads can still throw at runtime.

Line 256 and Line 275 only gate on .length; non-arrays with a length property (or null entries inside arrays) can still crash at Line 258 / Line 277 when reading .from. This path should be fully defensive if malformed data is expected.

🔧 Proposed defensive guards for array shape and entry shape
-		if (data.control?.length) {
-			data.control.forEach((control: IControlConnection) => {
-				if (!control.from || !nodeIdSet.has(control.from)) return;
+		if (Array.isArray(data.control) && data.control.length > 0) {
+			data.control.forEach((control: IControlConnection | unknown) => {
+				if (!control || typeof control !== 'object') return;
+				const c = control as IControlConnection;
+				if (!c.from || !nodeIdSet.has(c.from)) return;
 				edges.push({
 					...DEFAULT_EDGE,
 					id: uuid(),
-					source: control.from,
+					source: c.from,
 					target: node.id,
-					sourceHandle: `invoke-source.${control.classType}`,
+					sourceHandle: `invoke-source.${c.classType}`,
 					targetHandle: 'invoke-target',
 				});
 			});
 		}
@@
-		if (data.input?.length) {
-			data.input.forEach((input: IInputConnection) => {
-				if (!input.from || !nodeIdSet.has(input.from) || !input.lane) return;
+		if (Array.isArray(data.input) && data.input.length > 0) {
+			data.input.forEach((input: IInputConnection | unknown) => {
+				if (!input || typeof input !== 'object') return;
+				const i = input as IInputConnection;
+				if (!i.from || !nodeIdSet.has(i.from) || !i.lane) return;
 				edges.push({
 					...DEFAULT_EDGE,
 					id: uuid(),
-					source: input.from,
+					source: i.from,
 					target: node.id,
-					sourceHandle: `source-${input.lane}`,
-					targetHandle: `target-${input.lane}`,
+					sourceHandle: `source-${i.lane}`,
+					targetHandle: `target-${i.lane}`,
 				});
 			});
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared-ui/src/modules/flow/util/graph.ts` around lines 256 - 277,
The code currently assumes data.control and data.input are well-formed arrays
and that each entry has the expected shape, which can throw when a non-array or
malformed entry is present; update the guards to first verify
Array.isArray(data.control) and Array.isArray(data.input) before iterating, and
inside the loops validate each entry is a non-null object and has the required
properties (e.g., for control entries ensure typeof control.from === 'string' &&
nodeIdSet.has(control.from') and for input entries ensure typeof input.from ===
'string' && typeof input.lane !== 'undefined' && nodeIdSet.has(input.from'))
before calling edges.push (the code paths using DEFAULT_EDGE, uuid(),
sourceHandle/targetHandle) to prevent runtime crashes from malformed payloads.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/vscode/src/shared/util/pipelineParser.ts`:
- Around line 195-219: The input/control validation can throw or misclassify
errors because the code assumes each item is a non-null object and silently
ignores non-array shapes; update the validation in pipelineParser.ts so that
before iterating you verify comp.input and comp.control are arrays (otherwise
push an error like `Component "${compId}" has invalid "input"/"control" shape`),
and inside each forEach first ensure conn is an object (typeof conn === 'object'
&& conn !== null) before reading properties; then validate c.from is a string
and exists in idSet and (for input) validate c.lane is a string, pushing
specific errors referencing compId and connIdx rather than letting access throw
or be reported as generic JSON errors.

---

Outside diff comments:
In `@packages/shared-ui/src/modules/flow/util/graph.ts`:
- Around line 256-277: The code currently assumes data.control and data.input
are well-formed arrays and that each entry has the expected shape, which can
throw when a non-array or malformed entry is present; update the guards to first
verify Array.isArray(data.control) and Array.isArray(data.input) before
iterating, and inside the loops validate each entry is a non-null object and has
the required properties (e.g., for control entries ensure typeof control.from
=== 'string' && nodeIdSet.has(control.from') and for input entries ensure typeof
input.from === 'string' && typeof input.lane !== 'undefined' &&
nodeIdSet.has(input.from')) before calling edges.push (the code paths using
DEFAULT_EDGE, uuid(), sourceHandle/targetHandle) to prevent runtime crashes from
malformed payloads.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: e73324a0-02aa-4a61-a75b-39551ef7164b

📥 Commits

Reviewing files that changed from the base of the PR and between b6b2dc1 and 61c5d68.

📒 Files selected for processing (4)
  • apps/vscode/src/providers/views/PageEditor/PageEditor.tsx
  • apps/vscode/src/shared/util/pipelineParser.ts
  • packages/shared-ui/src/modules/flow/context/FlowGraphContext.tsx
  • packages/shared-ui/src/modules/flow/util/graph.ts

Comment on lines +195 to +219
if (Array.isArray(comp.input)) {
comp.input.forEach((conn: unknown, connIdx: number) => {
const c = conn as Record<string, unknown>;
if (!c.from || typeof c.from !== 'string') {
errors.push(`Component "${compId}" input connection at index ${connIdx} is missing a valid "from" field`);
} else if (!idSet.has(c.from)) {
errors.push(`Component "${compId}" input connection references non-existent component "${c.from}"`);
}
if (!c.lane || typeof c.lane !== 'string') {
errors.push(`Component "${compId}" input connection at index ${connIdx} is missing a valid "lane" field`);
}
});
}

// Validate control connections
if (Array.isArray(comp.control)) {
comp.control.forEach((conn: unknown, connIdx: number) => {
const c = conn as Record<string, unknown>;
if (!c.from || typeof c.from !== 'string') {
errors.push(`Component "${compId}" control connection at index ${connIdx} is missing a valid "from" field`);
} else if (!idSet.has(c.from)) {
errors.push(`Component "${compId}" control connection references non-existent component "${c.from}"`);
}
});
}
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.

⚠️ Potential issue | 🔴 Critical

Connection validation can still throw on malformed entries and misclassify errors.

Line 197 and Line 212 assume each array item is an object. Payloads like "input":[null] or "control":[42] will throw during validation, then get reported as “Invalid JSON” instead of actionable connection errors. Also, non-array input/control shapes are silently accepted.

🔧 Proposed hardening for shape + entry validation
 			record.components.forEach((component: unknown, index: number) => {
 				const comp = component as Record<string, unknown>;
 				const compId = (comp.id as string) || `index ${index}`;
 
 				// Validate input connections
-				if (Array.isArray(comp.input)) {
+				if ('input' in comp && !Array.isArray(comp.input)) {
+					errors.push(`Component "${compId}" field "input" must be an array`);
+				} else if (Array.isArray(comp.input)) {
 					comp.input.forEach((conn: unknown, connIdx: number) => {
-						const c = conn as Record<string, unknown>;
+						if (!conn || typeof conn !== 'object') {
+							errors.push(`Component "${compId}" input connection at index ${connIdx} must be an object`);
+							return;
+						}
+						const c = conn as Record<string, unknown>;
 						if (!c.from || typeof c.from !== 'string') {
 							errors.push(`Component "${compId}" input connection at index ${connIdx} is missing a valid "from" field`);
 						} else if (!idSet.has(c.from)) {
 							errors.push(`Component "${compId}" input connection references non-existent component "${c.from}"`);
 						}
 						if (!c.lane || typeof c.lane !== 'string') {
 							errors.push(`Component "${compId}" input connection at index ${connIdx} is missing a valid "lane" field`);
 						}
 					});
 				}
 
 				// Validate control connections
-				if (Array.isArray(comp.control)) {
+				if ('control' in comp && !Array.isArray(comp.control)) {
+					errors.push(`Component "${compId}" field "control" must be an array`);
+				} else if (Array.isArray(comp.control)) {
 					comp.control.forEach((conn: unknown, connIdx: number) => {
-						const c = conn as Record<string, unknown>;
+						if (!conn || typeof conn !== 'object') {
+							errors.push(`Component "${compId}" control connection at index ${connIdx} must be an object`);
+							return;
+						}
+						const c = conn as Record<string, unknown>;
 						if (!c.from || typeof c.from !== 'string') {
 							errors.push(`Component "${compId}" control connection at index ${connIdx} is missing a valid "from" field`);
 						} else if (!idSet.has(c.from)) {
 							errors.push(`Component "${compId}" control connection references non-existent component "${c.from}"`);
 						}
 					});
 				}
 			});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/vscode/src/shared/util/pipelineParser.ts` around lines 195 - 219, The
input/control validation can throw or misclassify errors because the code
assumes each item is a non-null object and silently ignores non-array shapes;
update the validation in pipelineParser.ts so that before iterating you verify
comp.input and comp.control are arrays (otherwise push an error like `Component
"${compId}" has invalid "input"/"control" shape`), and inside each forEach first
ensure conn is an object (typeof conn === 'object' && conn !== null) before
reading properties; then validate c.from is a string and exists in idSet and
(for input) validate c.lane is a string, pushing specific errors referencing
compId and connIdx rather than letting access throw or be reported as generic
JSON errors.

@stepmikhaylov
Copy link
Copy Markdown
Collaborator

@Rod-Christensen
The /validate endpoint of EaaS services already provides all necessary checks. VSCode extension should use the /validate endpoint rather than implement it's own validation logic, shouldn't is?

@charliegillet
Copy link
Copy Markdown
Contributor Author

@stepmikhaylov Good point — using the /validate endpoint from EaaS services instead of duplicating validation logic in the VSCode extension would be cleaner and keep validation in a single source of truth.

Should we rework this PR to use /validate instead of the client-side validation, or would you prefer to close this and open a fresh one with that approach? Happy to go either direction based on your guidance.

@stepmikhaylov
Copy link
Copy Markdown
Collaborator

@charliegillet,

Thank you for contribution. Pipeline validation is implemented on Server side and can be leveraged by both Python and TypeScript RocketRide clients with validate method. The changes are unnecessary, PR is closed.

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

Labels

module:ui Chat UI and Dropper UI module:vscode VS Code extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid pipe error crashes canvas UI with unclear error message

2 participants