Error "The script object is no longer valid." when trying to append video filter component

I’m in the process of getting my feet wet with UXP in Premiere and porting over some older ExtendScript and CEP utilities.
I’ve encountered an issue when trying to apply effects to clip items. Ultimately, I am trying to apply an effect a user selects from a modal dialog in a command to all selected clip items in a sequence. Essentially:

async #handleSelection(matchName: string) {
	this.dialog?.close();

	const project = await app.Project.getActiveProject();
	if (!project) {
		throw new Error("No active project");
	}

	const sequence = await project.getActiveSequence();
	if (!sequence) {
		throw new Error("No active sequence");
	}

	const selection = await sequence.getSelection();
	const items = await selection.getTrackItems();
	// const items = (await sequence.getVideoTrack(0)).getTrackItems(1, false);

	appendEffect(project, matchName, items)
}

async function appendEffect(project, effectMatchName, items) {
	try {
		for (const item of items) {
			// Not sure if a component needs to be created for each use or can be shared
			const component = await app.VideoFilterFactory.createComponent(effectMatchName);
			const componentChain = await item.getComponentChain();
			const action = componentChain.createAppendComponentAction(component);
			executeAction(project, action)
		}
	}
	catch (e) {
		throw new Error(e.message);
	}
}

function executeAction(project, action) {
	try {
		project.lockedAccess(() => 
			project.executeTransaction(compoundAction => {
				compoundAction.addAction(action);
			})
		)
	}
	catch (e) {
		throw new Error(`Error executing action: ${e.message}`);
	}
}

Execution fails on the line:
const action = componentChain.createAppendComponentAction(component);
with the error:
C:\Users\nthArt\workspace\adobe_uxp\com.nthArt.pr-effects\dist\pt-cmd-effects.js:82 Error: The script object is no longer valid.
I thought the issue might be related to stale native references in the async loop, so I tried just applying an effect to the first VideoClipTrackItem in the sequence as well, but it failed similarly.

Any assistance or advice would be appreciated.

I am developing against Premiere 26.3.0 using UDT 2.2.1.2 on Windows 11 25H2 26200.8655.

I think the issue might be that the action is being created before the transaction executes.

Try moving the createAppendComponentAction() call inside the executeTransaction() callback, and create/add the action synchronously within the transaction.

One sample panel follows this pattern in addEffects() and addMultipleEffects(): sample-panel

They use createInsertComponentAction(), but the transaction flow is the same. Please let me know if this helps.

1 Like

Thanks for the advice! Indeed, I did need to call createAppendComponentAction() inside executeTransaction(), as well as refactor some other things that I had added for code reusability. It actually seems that it just needs to be within the access lock, not the transaction itself, which makes sense for reference stability. (Curiously, in this older tutorial by NT Productions, calling createAppendComponentAction() outside of the lockedAccess/transaction does work for him.) Going forward, I’ll assume that all synchronous calls to create Actions should be done within the same lockedAccess scope as the transaction. (I’ll think of it as a mutex.)

Not unsurprisingly, it’s easy for native references to get stale in the unlocked, asynchronous scope. I had tried doing some mapping in Promise.all() to get the componentChains from the list of selected TrackItems, but this also seemed to produce stale references by the time I got around to the transaction.

Is the general principle to try to aggregate as many mutations within a single transaction? It seems like each compoundAction/invocation of executeTransaction() is an individual undo step. However, it’s proving tricky to batch all operations into a single executeTransaction() to create a single undoable operation for a UXP command.

Glad you got it working.

Your batching idea is right. A single executeTransaction() creates a single undo step, and a single compoundAction can contain multiple addAction() calls. That’s the same pattern used in the sample’s addMultipleEffects() example.

The main thing is to do all the async work up front (createComponent(), getComponentChain(), etc.), then create and add all the actions inside one transaction. That gives you one clean undo step for the whole operation.

On the stale reference side, I wouldn’t assume Promise.all() or pre-fetching component chains was the issue. The sample fetches component chains and creates components before entering lockedAccess(), then uses them inside the transaction. I also tested fetching chains with Promise.all() and using them later in the transaction without any issues.

The NT Productions example you linked also creates the action before entering lockedAccess(), so that part by itself doesn’t seem to be a problem. I wasn’t able to narrow down the exact cause of the original error, though, so I don’t want to speculate too much there.

One other thing to watch out for: selection.getTrackItems() can return both video and audio items. If you try to append a video filter to an audio chain, you’ll get an Invalid parameter error. So it’s worth filtering to video items first before building the actions.

Hope that helps.