Skip to Content
LearnTroubleshootingCommon Errors

Common Errors

This guide lists the warnings and errors Vue Flow can emit, what triggers them, and how to fix them. We also collect common questions and pitfalls from our Discord Server , Github Issues  and Github Discussions .

How Vue Flow reports errors

Every error is a VueFlowError with a code (an ErrorCode), a human-readable message, and the args used to build it. In development Vue Flow logs these to the console (prefixed with [Vue Flow]); in production it stays quiet.

To react to them yourself, for logging or a toast, listen to the @error event on <VueFlow>. Narrow a specific one with isErrorOfType or by checking error.code:

<script setup lang="ts"> import { ErrorCode, isErrorOfType, VueFlow, type VueFlowError } from '@xyflow/vue'; import '@xyflow/vue/style.css'; function onError(error: VueFlowError) { if (isErrorOfType(error, ErrorCode.MISSING_STYLES)) { // handle a specific error, e.g. surface it to the user } console.error(error.code, error.message); } </script> <template> <VueFlow :nodes="nodes" :edges="edges" @error="onError" /> </template>

One error is thrown rather than emitted: USE_VUE_FLOW_OUTSIDE_PROVIDER. It never reaches the @error handler because there is no store to report through yet.

Setup errors

useVueFlow() was called without a provider

Code: USE_VUE_FLOW_OUTSIDE_PROVIDER (thrown)

useVueFlow() and useVueFlowStore() need a Vue Flow store. A store is created by <VueFlow>, by a setupVueFlow() call, or by a <VueFlowProvider>. Calling them where none of those has provided one, or outside a component’s setup, throws this error.

This throws, because the store doesn’t exist yet:
<script setup lang="ts"> import { useVueFlow, VueFlow } from '@xyflow/vue'; import { shallowRef } from 'vue'; import '@xyflow/vue/style.css'; // the store is created by <VueFlow> below, so it isn't available here const { getZoom } = useVueFlow(); const nodes = shallowRef([]); const edges = shallowRef([]); </script> <template> <VueFlow :nodes="nodes" :edges="edges" /> </template>

Preferred: call setupVueFlow() instead of useVueFlow(). It creates the store, provides it to the subtree, and returns the same instance, so the <VueFlow> below reuses it:

<script setup lang="ts"> import { setupVueFlow, VueFlow } from '@xyflow/vue'; import { shallowRef } from 'vue'; import '@xyflow/vue/style.css'; // creates and provides the store, and returns the instance to use right here const { getZoom } = setupVueFlow(); const nodes = shallowRef([]); const edges = shallowRef([]); </script> <template> <VueFlow :nodes="nodes" :edges="edges" /> </template>

Or move the useVueFlow() call into a child rendered inside <VueFlow>:

App.vue
<script setup lang="ts"> import { VueFlow } from '@xyflow/vue'; import { shallowRef } from 'vue'; import '@xyflow/vue/style.css'; import FlowControls from './FlowControls.vue'; const nodes = shallowRef([]); const edges = shallowRef([]); </script> <template> <VueFlow :nodes="nodes" :edges="edges"> <FlowControls /> </VueFlow> </template>
FlowControls.vue
<script setup lang="ts"> import { useVueFlow } from '@xyflow/vue'; // works, because this component is a child of <VueFlow> const { getZoom } = useVueFlow(); </script>

Or wrap the tree in a <VueFlowProvider>, the component form of setupVueFlow(), when several sibling components above <VueFlow> need to share one store:

Wrapper.vue
<script setup lang="ts"> import { VueFlowProvider } from '@xyflow/vue'; import App from './App.vue'; </script> <template> <VueFlowProvider> <App /> </VueFlowProvider> </template>

It seems that you haven’t loaded the necessary styles

Code: MISSING_STYLES

Vue Flow needs its stylesheet to position and render the graph. Import it once, near your app entry:

import '@xyflow/vue/style.css';

If you only want the minimum required rules and bring your own theme, import the base styles instead (@xyflow/vue/base.css). See theming for the difference.

The Vue Flow parent container needs a width and a height

Code: MISSING_VIEWPORT_DIMENSIONS

Vue Flow measures its parent element to size the renderer. A wrapper with no height leaves nothing to render into.

This has no height, so the graph never appears:
<script setup lang="ts"> import { VueFlow } from '@xyflow/vue'; import '@xyflow/vue/style.css'; </script> <template> <div> <VueFlow /> </div> </template>
Give the wrapper a fixed or inherited size:
<script setup lang="ts"> import { VueFlow } from '@xyflow/vue'; import '@xyflow/vue/style.css'; </script> <template> <div style="height: 100vh; width: 100vw"> <VueFlow /> </div> </template>

Node errors

Node type is missing

Code: NODE_TYPE_MISSING

A node has a type with no matching component, so Vue Flow can’t render it. The idiomatic fix is to register the type through a #node-<type> slot, where the slot name must match the node’s type exactly:

<template> <VueFlow :nodes="nodes" :edges="edges"> <template #node-custom="props"> <CustomNode v-bind="props" /> </template> </VueFlow> </template>

You can also register components with the nodeTypes prop. The key has to match the node’s type exactly (custom, not Custom):

<script setup lang="ts"> import { VueFlow } from '@xyflow/vue'; import CustomNode from './CustomNode.vue'; const nodeTypes = { custom: CustomNode }; const nodes = [{ id: '1', type: 'custom', position: { x: 0, y: 0 }, data: {} }]; </script> <template> <VueFlow :nodes="nodes" :node-types="nodeTypes" /> </template>

Node is invalid

Code: NODE_INVALID

A node object failed validation while Vue Flow was adding it to the store. Every node needs a unique string id and a position:

const nodes = [{ id: 'n1', position: { x: 0, y: 0 }, data: {} }];

Node not found

Code: NODE_NOT_FOUND

You looked up a node id that isn’t in the graph, for example with useNode(id) or an instance getter. It also fires when a handler runs after its node was removed, so read the node up front rather than inside a delayed callback.

Node is missing a parent

Code: NODE_MISSING_PARENT

A node’s parentId points to a node that doesn’t exist, or that appears after the child in the array. List the parent before its children so it’s in the lookup first:

const nodes = [ { id: 'group', position: { x: 0, y: 0 }, data: {} }, { id: 'child', parentId: 'group', position: { x: 20, y: 20 }, data: {} }, ];

Node id is not unique

Code: NODE_DUPLICATE_ID

Two nodes share the same id, so the later one overwrites the earlier one in the internal lookup. Give every node a unique id.

Edge errors

An edge needs a source and a target

Code: EDGE_INVALID

An edge was created without a source or a target, so there’s nothing to connect.

Missing endpoints:
const edges = [{ id: 'e1' }];
Both endpoints set:
const edges = [{ id: 'e1', source: 'n1', target: 'n2' }];

Edge source or target is missing

Codes: EDGE_SOURCE_MISSING, EDGE_TARGET_MISSING, EDGE_SOURCE_TARGET_MISSING

The edge’s source and/or target references a node id that isn’t in the graph. Make sure both nodes exist, and that the ids match exactly (they’re case-sensitive).

Edge type is missing

Code: EDGE_TYPE_MISSING

An edge has a type with no matching component. Register it through an #edge-<type> slot (the slot name matching the edge’s type), or through the edgeTypes prop:

<template> <VueFlow :nodes="nodes" :edges="edges"> <template #edge-custom="props"> <CustomEdge v-bind="props" /> </template> </VueFlow> </template>

Edge id is not unique

Code: EDGE_DUPLICATE_ID

Two edges share the same id, so the later one overwrites the earlier one. Give every edge a unique id.

Edge not found

Code: EDGE_NOT_FOUND

You referenced an edge that isn’t in the state, most often when reconnecting an edge that was already removed, or in a click/selection handler that runs after the edge was deleted. Read the edge before the async gap that might remove it.

Composable errors

useNodeConnections: No node ID found

Code: NODE_CONNECTIONS_MISSING_ID

useNodeConnections reads the current node from context. Call it inside a custom node, or pass an explicit id:

const connections = useNodeConnections({ id: 'n1' });

Other common issues

Mouse events aren’t working consistently when my nodes contain a <canvas /> element

If you use a <canvas /> inside a custom node, you might get incorrect coordinates in mouse events from the canvas.

Vue Flow uses CSS transforms to scale nodes as you zoom, but from the DOM’s perspective the element keeps its original size. That mismatch throws off any listener that computes the mouse position relative to the canvas.

In handlers you control, scale your computed relative position by 1 / zoom, where zoom is the current zoom level. Read it with getZoom from useVueFlow.

Edges are not displaying

If your edges don’t show up, check the following:

  • You imported the stylesheet (import '@xyflow/vue/style.css';).
  • If you replaced the default node with a custom node, make sure it renders source/target handles. An edge can’t attach without a handle.
  • If you use a styling library like Tailwind, make sure it doesn’t override the edge styles. Some libraries set overflow: hidden on the .vue-flow__edges SVG selector, which hides the edges.
  • After an async operation (like a backend request) that changes handles, call updateNodeInternals from useVueFlow so Vue Flow re-measures the handle positions.

Edges are not displaying correctly

If your edges render but look wrong:

  • To hide handles, use opacity: 0 or visibility: hidden, not display: none.
  • If an edge connects to the wrong handle, check for more than one handle of the same type (source or target) on the node. Give same-type handles distinct ids so Vue Flow knows which one an edge belongs to.
  • If you move handles around (reordering and such), call updateNodeInternals from useVueFlow afterwards so Vue Flow re-measures them.
  • In a custom edge, pass the sourceX, sourceY, targetX, and targetY props into your path function (for example getBezierPath). Those are the coordinates of the source and target handles.
  • If an edge enters or exits a handle at an odd angle, also pass the sourcePosition and targetPosition props into the path function so it starts and ends against the handle correctly.
Last updated on