nocobase/nocobase · error · Error
flowModels:attach position target is not a sibling under the
Error message
flowModels:attach position target is not a sibling under the same parent/subKey
What it means
Thrown by FlowModelRepository.attach when the before/after anchor target does not exist as a depth-1 child of the given parentId with the same subKey (type). The anchor must be an existing sibling in the exact same parent/subKey bucket; the SQL existence check returned no rows.
Source
Thrown at packages/plugins/@nocobase/plugin-flow-engine/src/server/repository.ts:1980
ON NodeInfo.descendant = TreeTable.descendant
AND NodeInfo.depth = 0
WHERE TreeTable.depth = 1
AND TreeTable.ancestor = :ancestor
AND TreeTable.descendant = :descendant
AND NodeInfo.type = :type
LIMIT 1`,
{
type: 'SELECT',
replacements: {
ancestor: parentId,
descendant: target,
type: subKey,
},
transaction,
},
);
if (!ok?.length) {
throw new Error('flowModels:attach position target is not a sibling under the same parent/subKey');
}
}
// 清理旧路径缓存(旧祖先)
await this.clearXUidPathCache(modelUid, transaction);
// 更新 root options:确保 nodesToModel 能按 subKey/subType 正确挂载
await modelInstance.update(
{
options: {
...lodash.omit(modelInstance.get('options') as any, ['uid']),
parentId,
parent: parentId,
subKey,
subType,
},
},
{ transaction, hooks: false },View on GitHub (pinned to fa42722fef)
Solutions
- Re-fetch the current children of parentId for the given subKey and use one of their uids as target.
- Verify the target is attached under the same parentId AND same subKey before anchoring.
- Fall back to position 'last' when no valid sibling anchor is available.
Example fix
// before
await repo.attach({ uid, parentId, subKey, subType: 'array', position: { type: 'after', target: staleUid } });
// after
const siblings = await repo.findChildren(parentId, subKey);
const position = siblings.some((s) => s.uid === targetUid)
? { type: 'after', target: targetUid }
: 'last';
await repo.attach({ uid, parentId, subKey, subType: 'array', position }); Defensive patterns
Strategy: validation
Validate before calling
const siblings = await repo.findChildren(parentId, subKey);
const anchorOk = typeof position === 'object' && siblings.some((s) => s.uid === position.target);
await repo.attach({ uid, parentId, subKey, subType, position: anchorOk ? position : 'last' }); Type guard
const isExistingSibling = (siblings: { uid: string }[], target: string) =>
siblings.some((s) => s.uid === String(target).trim()); Try / catch
try {
await repo.attach(opts);
} catch (e) {
if (String(e.message).includes('not a sibling under the same parent/subKey')) {
await refreshSiblings();
await repo.attach({ ...opts, position: 'last' });
} else throw e;
} Prevention
- Re-fetch the sibling list right before attaching; never cache anchors across requests
- Verify the anchor shares both parentId and subKey, not just ancestry
- Handle concurrent deletions by falling back to 'last'
When it happens
Trigger: Calling flowModels.attach with position { type:'before'|'after', target } where the target uid is: not yet attached, attached under a different parent, attached under the same parent but a different subKey, or simply misspelled/deleted.
Common situations: Anchoring against a node that was just deleted in another tab; anchoring against a cousin node (same grandparent, different parent); race between fetching sibling list and attaching; uid typo after copy-paste.
Related errors
- flowModels:attach subKey '${subKey}' already exists on paren
- 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/eb3e16438819668c.
Report an issue: GitHub.