Try catch is not catching exception error


I’m getting the following error when I try to set the pluginData property:

Plugin Error: Plugin xyz is not permitted to make changes from the background. Return a Promise to continue execution asynchronously.
    at convertPluginErrorToString (plugins/PluginErrorUtil.js:1:198)
    at internalFormatPluginError (plugins/PluginErrorUtil.js:1:503)
    at internalReportPluginError (plugins/PluginErrorUtil.js:1:610)
    at Object.reportPluginError (plugins/PluginErrorUtil.js:1:1042)
    at Object.checkAllowedToEdit (plugins/ScenegraphGuard.js:1:1178)
    at Artboard.<anonymous> (plugins/ScenegraphWrappers.js:1:11133)
    at setElementPluginData (/Users/user/Library/Application Support/Adobe/Adobe XD CC/develop/plugin/main.js:2556:23)

I know it’s outside of the edit context so I’ve placed a try catch around it but it’s still showing up in the console.

Example code:

try {
	// get an instance to a non selected artboard and set it's pluginData property
	artboard.pluginData = JSON.stringify({x:10,y:20});
catch(error) {
	// nothing here

Related question. Is there a way to check if a sceneNode is in an editable context?


used on an object inside a group:

Group ('Group 3356') { width: 100, height: 100 global X,Y: -498, 987 parent: Artboard ('iPhone 6/7/8 – 7') children: [Rectangle, Path] }

could it solve allowing to check the edit context’s and/or the parent’s properties?


Plugin xyz is not permitted to make changes from the background (in my experience) doesn’t have anything to do with the edit context, but with handling asynchronous code.

While I agree that the error probably shouldn’t show up when the code is in a try/catch block, this probably is unrelated to the edit context (are you sure you’re handling asynchronous bits of the code correctly?).

According to the documentation, pluginData also isn’t affected by the edit context –cf. the pluginData section of the docs.

Therefore, I “second” that the error should get caught by the try/catch block, but I don’t think that this is realted to the edit context in any way :wink:


There’s this line from the docs:

To store general metadata for the document overall, set pluginData on the root node of the scenegraph. Metadata on the root node can be changed from any edit context.

So that states the root node is settable from any context but what about the nodes that are not the root node?

If you’ll also notice the call stack shows the Object.checkAllowedToEdit

at Object.checkAllowedToEdit (plugins/ScenegraphGuard.js:1:1178)
at Artboard.<anonymous> (plugins/ScenegraphWrappers.js:1:11133)

I’m not making the case to change it but I can’t see any strong arguments why pluginData needs an edit context if you can set the root node plugin data and if plugin data only affects the current plugin.

Here’s another thing. The code above is looping through all of the artboards. The focused artboard does not throw an error but the other artboards do. So is that async or edit context? :confused:


Cool! I didn’t know about that property.

In some languages, container objects that contain child objects usually have a parentNode.owns(node) or parentNode.contains(node) like method.

I don’t see any methods like that on Group. That would be a good addition.


Yep – sorry, I misread this section of the docs.

Ok – that really is strange. If I remember correctly, edit context errors look different – that might be a “deeper” bug than I’d thought it would be (unfitting error messages definitely sound rather strange :laughing: ) – do you do something asynchronous in the plugin (i.e. could it be a problem with the async stuff)?


Couldn’t you just use group.children.includes(myNode) for that? Since that’s ES2016, I don’t know if it’s supported by XD, but otherwise, the more legacy group.children.indexOf(myNode) != -1 should also work…


I can give that a try (get it)?

If I need to I can fall back on walking down the nodes in the edit context and comparing each node via equality. Something like:

if ( return true;


I made a method to test if a node is a descendant of another node but found an issue.

 * Check if node is in edit context
function isInEditContext(selection, node) {
	var editContext = selection.editContext;

	console.log("Checking context:" + editContext.toString());

	if (editContext==globalModel.documentRoot) {
		console.log ("edit context is document root. edit allowed")
		return true;

	if (editContext==node) {
		console.log ("edit context is current node. edit allowed")
		return true;

	if (editContext.isContainer && isChildNode(node)) {
		console.log ("edit context owns current node. edit allowed.")
		return true;

	console.log ("node outside of edit context. edit not allowed.")
	return false;

function isChildNode(parentNode, node) {

	if (parentNode==node) {
		return true;

	if (parentNode.isContainer) {
		var childNodes = parentNode.children;

		for(var i=0;i<childNodes.length;i++) {
			let childNode =;

			// found the node
			if (childNode==node) {
				return true;

			if (childNode.isContainer) {
				return isChildNode(childNode, node);

	return false;

I think that the selection.editContext node is always the root node.


According to the documentation, that is to be expected as long as you haven’t selected a node inside a group (although I don’t really see the logic behind that :wink:) :

If the user hasn’t drilled into any container node, this value is the document root, and its scope includes all immediate children of the pasteboard (including Artboards), and all immediate children of all those Artboards.

You’re probably better off using the “plain” selection…


Hmm… I thought it was the selected item and it’s descendants. I’ll do more tests.

So if I select Artboard 1 I can create a rectangle on Artboard 2?

What do you mean plain selection? Is my function incorrect?


yes while selecting an scenenode gives access to its linear mates means artboard selection gives access to other artboards and their children this is because atleast selection for a document is artboard to qualify it to be a selection


FWIW I figured out the cause of the error. In short I was missing an await keyword along the call path.

I have two commands that call the same function:

module.exports = {
	commands: {
		showMainDialog: showMainDialog, 
		showOptionsDialog: showMainDialogWithOptions

The second command function showMainDialogWithOptions() calls the first function but passes a parameter to the first command function. In showMainDialogWithOptions() the line where I called the first function, showMainDialog() did not have the await keyword before hand:

async function showMainDialogWithOptions(selection, documentRoot) {

  try {
	 // original line:
	 // showMainDialog(selection, documentRoot, true);
	 await showMainDialog(selection, documentRoot, true); // correct
  catch(error) {