nocobase/nocobase · error
Failed to copy popup from template
Error message
Failed to copy popup from template
What it means
Immediately after duplicating the template's popup model in doConvert, the plugin unwraps the new uid from the duplicateModel response (duplicated?.uid, data.uid, data.data.uid). If no uid can be extracted, the conversion cannot proceed (nothing to point the new open-view at), so this localized error is thrown.
Source
Thrown at packages/plugins/@nocobase/plugin-ui-templates/src/client-v2/menuExtensions.tsx:893
const row = unwrap(res);
return row && typeof row === 'object' ? (row as Record<string, any>) : null;
};
const inferFromTemplateRow = (tplRow: Record<string, any>): PopupTemplateContextFlags => {
const scene = resolveActionScene((use: string) => model.flowEngine?.getModelClass?.(use), tplRow?.useModel);
return inferPopupTemplateContextFlags(scene, tplRow?.filterByTk, tplRow?.sourceId);
};
const doConvert = async () => {
const tplRow = await fetchTemplateRow();
const targetUid = normalizeStr(tplRow?.targetUid) || normalizeStr(openViewParams?.uid);
if (!targetUid) {
throw new Error(tNs('Popup template not found'));
}
const duplicated = await model.flowEngine.duplicateModel(targetUid);
const newUid = duplicated?.uid || duplicated?.data?.uid || duplicated?.data?.data?.uid;
if (!newUid) {
throw new Error(tNs('Failed to copy popup from template'));
}
const inferred: PopupTemplateContextFlags = tplRow
? inferFromTemplateRow(tplRow)
: extractPopupTemplateContextFlagsFromParams(openViewParams);
const nextOpenView: any = { ...(openViewParams || {}), uid: newUid };
delete (nextOpenView as any).popupTemplateUid;
nextOpenView.popupTemplateContext = true;
delete (nextOpenView as any).popupTemplateHasFilterByTk;
delete (nextOpenView as any).popupTemplateHasSourceId;
// 同步清理 params 侧的 filterByTk/sourceId,避免 record action 复用 collection 弹窗时泄漏 filterByTk
if (!inferred.hasFilterByTk && 'filterByTk' in nextOpenView) {
delete nextOpenView.filterByTk;
}
if (!inferred.hasSourceId && 'sourceId' in nextOpenView) {
delete nextOpenView.sourceId;
}
model.setStepParams('popupSettings', { [openViewStepKey]: nextOpenView });View on GitHub (pinned to fa42722fef)
Solutions
- Inspect the duplicateModel request/response in devtools; fix the server-side duplication failure (permissions, resource limits).
- Align @nocobase/client-v2 and flow-engine versions with the server so response unwrapping matches.
- Retry the conversion action after any transient network/API failure.
- As a workaround, manually duplicate the popup block and re-link the reference to the new copy.
Example fix
// before: response envelope mismatch
const newUid = duplicated?.uid || duplicated?.data?.uid; // undefined -> throws
// after: ensure client/server versions match so envelope is { data: { uid } }
// yarn why @nocobase/client-v2 # verify single consistent version Defensive patterns
Strategy: try-catch
Validate before calling
const duplicated = await model.flowEngine.duplicateModel(targetUid);
const newUid = duplicated?.uid || duplicated?.data?.uid || duplicated?.data?.data?.uid;
if (!newUid) {
// duplication succeeded shape-wise but returned no uid — treat as failure, do not save template
return message.error('Copy failed; check server duplicate endpoint response.');
} Type guard
function extractUid(v: unknown): string | null {
for (const o = v; o && typeof o === 'object'; ) {
if (typeof (o as any).uid === 'string' && (o as any).uid) return (o as any).uid;
o = (o as any).data;
}
return null;
} Try / catch
try {
const newUid = await convertFromTemplate(targetUid);
} catch (e) {
if (e.message.includes('Failed to copy popup from template')) {
// inspect duplicateModel response in devtools; check server logs
} else throw e;
} Prevention
- Verify duplicateModel response envelope after any flow-engine upgrade.
- Ensure no API middleware rewrites resource response shapes.
- Retry conversions after transient network failures.
When it happens
Trigger: Calling the 'convert popup template reference to independent copy' action when flowEngine.duplicateModel(targetUid) resolves but its response carries no uid in any nested data layer — server-side duplication failed partially, response shape mismatch, or the request was intercepted/short-circuited.
Common situations: Client (client-v2 / flow-engine) and server version mismatch changing the duplicateModel response envelope; API middleware stripping or reshaping the response; server error returned as an empty object after a failed deep copy; rate-limit or auth expiry producing an empty 2xx-style response.
Related errors
- Failed to duplicate pop-up
- Target block is invalid
- No rows selected for deletion.
- [flow-engine] snippet not found: ${ref}
- FLOW_SURFACE_BAD_REQUEST
AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01).
Data as JSON: /api/errors/d8397120e51e6b8c.
Report an issue: GitHub.