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.
<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>:
<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><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:
<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.
<script setup lang="ts">
import { VueFlow } from '@xyflow/vue';
import '@xyflow/vue/style.css';
</script>
<template>
<div>
<VueFlow />
</div>
</template><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.
const edges = [{ id: 'e1' }];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/targethandles. 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: hiddenon the.vue-flow__edgesSVG selector, which hides the edges. - After an async operation (like a backend request) that changes handles, call
updateNodeInternalsfromuseVueFlowso Vue Flow re-measures the handle positions.
Edges are not displaying correctly
If your edges render but look wrong:
- To hide handles, use
opacity: 0orvisibility: hidden, notdisplay: none. - If an edge connects to the wrong handle, check for more than one handle of the same type
(
sourceortarget) on the node. Give same-type handles distinctids so Vue Flow knows which one an edge belongs to. - If you move handles around (reordering and such), call
updateNodeInternalsfromuseVueFlowafterwards so Vue Flow re-measures them. - In a custom edge, pass the
sourceX,sourceY,targetX, andtargetYprops into your path function (for examplegetBezierPath). 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
sourcePositionandtargetPositionprops into the path function so it starts and ends against the handle correctly.