nocobase/nocobase · error
flowModels:attach subKey '${subKey}' already exists on paren
Error message
flowModels:attach subKey '${subKey}' already exists on parent '${parentId}' What it means
Thrown by FlowModelRepository.attach when the model being attached has subType 'object' and another child model already occupies the same subKey under the target parent. Object sub-models are keyed maps, so each subKey must be unique per parent; attaching a second model with the same key would silently overwrite the existing child, so the repository rejects it.
Source
Thrown at packages/plugins/@nocobase/plugin-flow-engine/src/server/repository.ts:1930
ON NodeInfo.descendant = TreeTable.descendant
AND NodeInfo.depth = 0
WHERE TreeTable.depth = 1
AND TreeTable.ancestor = :ancestor
AND NodeInfo.type = :type
AND TreeTable.descendant != :uid
LIMIT 1`,
{
type: 'SELECT',
replacements: {
ancestor: parentId,
type: subKey,
uid: modelUid,
},
transaction,
},
);
if (conflict?.length) {
throw new Error(`flowModels:attach subKey '${subKey}' already exists on parent '${parentId}'`);
}
}
const normalizePosition = (input: unknown): FlowModelAttachPosition => {
if (!input) return 'last';
if (input === 'first' || input === 'last') return input;
if (typeof input === 'object') {
const p = input as any;
const type = p?.type;
const target = String(p?.target || '').trim();
if ((type === 'before' || type === 'after') && target) {
return { type, target };
}
}
throw new Error('flowModels:attach invalid position');
};
const position: FlowModelAttachPosition =View on GitHub (pinned to fa42722fef)
Solutions
- Query the existing children of parentId first and pick an unused subKey, or delete/detach the conflicting child before re-attaching.
- If the model is being moved back to a former parent, ensure the previous attach's transaction completed/rolled back so stale tree rows were removed.
- Catch the error and surface it to the user as 'a child with this key already exists' rather than retrying blindly.
Example fix
// before
await repo.attach({ uid, parentId, subKey: 'items', subType: 'object' });
// after
const children = await repo.findChildren(parentId);
if (children.some((c) => c.subKey === 'items')) subKey = 'items-2';
await repo.attach({ uid, parentId, subKey, subType: 'object' }); Defensive patterns
Strategy: validation
Validate before calling
const children = await repo.findChildren(parentId);
if (subType === 'object' && children.some((c) => c.subKey === subKey && c.uid !== uid)) {
throw new SkipAttachError(`subKey '${subKey}' taken`);
}
await repo.attach({ uid, parentId, subKey, subType }); Type guard
const isFreeSubKey = (c: { subKey: string; uid: string }[], subKey: string, uid: string) =>
!c.some((x) => x.subKey === subKey && x.uid !== uid); Try / catch
try {
await repo.attach({ uid, parentId, subKey, subType: 'object' });
} catch (e) {
if (String(e.message).startsWith('flowModels:attach subKey')) {
notifyUser('A child with this key already exists here.');
} else throw e;
} Prevention
- List existing children of the parent before choosing an object subKey
- Generate unique subKeys (suffix -2, -3) instead of reusing well-known keys
- Clean up tree rows when detaching so old entries cannot conflict later
When it happens
Trigger: Calling flowModels.attach (or the flowModels:attach server action) with subType='object' where a sibling row already exists at depth=1 under parentId with NodeInfo.type === subKey, and that sibling's uid differs from the model being attached.
Common situations: Re-attaching a moved model after a failed/partial cleanup left the old row in the tree table; two UI panels concurrently creating a child under the same well-known key like 'detail' or 'form'; copying a model definition without renaming its subKey.
Related errors
- flowModels:attach position target is not a sibling under the
- Cannot set itself as the parent node
- FLOW_SURFACE_BAD_REQUEST
- FLOW_SURFACE_BAD_REQUEST
- flowSurfaces field container '${containerUse}' is not suppor
AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01).
Data as JSON: /api/errors/7fcca27cdc4b34ae.
Report an issue: GitHub.