UXP panel always fails with “Cannot resolve module: main.js” — even for minimal plugin

Hi everyone,

I’m experiencing a persistent issue when trying to load a basic UXP panel plugin in Photoshop — both in “Add Plugin” mode and in Webpack-based projects.

No matter what I do, the plugin fails to load and gives me this error:

Uncaught Error: Cannot resolve module: main.js

This happens even when I try the simplest possible plugin:

  • manifest.json (with manifestVersion 5)

  • index.html with a single <sp-button> and inline JS

  • No main.js defined anywhere in the manifest

Here’s what I’ve already ruled out:

  • No "main" field in the manifest

  • No syntax errors in index.html

  • Plugin is added via “Add Plugin” from the dist/ folder

  • Tried both minimal plugin (no build) and Webpack-based plugin

  • Tried Photoshop 25.x and also reverted back to version 22.2

  • Tried with UXP Developer Tool v1.7.0+

  • All files (manifest, html, js) are in correct locations

  • Tried from local drive (not UNC path)

  • Even test plugins with just a button and text result in this error

This issue has been consistent across plugin types — even when index.js is completely empty or not referenced.

What might be causing UXP to expect a main.js file that doesn’t exist?
Is this fallback behavior? A bug in UXP? Something environmental I’m overlooking?

Any help or ideas would be deeply appreciated!

Thanks :folded_hands:
— Lior

I just use vanilla JavaScript for my plugins and have the following line in my index.html file

<script src="index.js"></script>

which is the JavaScript file that is the backend for the HTML.

Not sure if that will help or not.

Also, UXP Developer Tool has a File > Create Plugin option that might also be useful for getting started.

Thanks!

I tried both simple and Webpack-based plugins — even a super minimal one with just a button and no build or imports. Still getting the same main.js error, and the panel stays empty.

At this point I’m thinking —
Could this be caused by company-level restrictions or missing admin rights?

Like maybe:

  • UXP WebView is blocked?

  • UXP Dev Tool isn’t running with enough permissions?

  • Something in Photoshop is sandboxed?

Anyone ran into this kind of thing before?

Appreciate any ideas!
— Lior

If you package that up and post it here, I’d be happy to test it on my machine – to see whether I see the same problem.

Philip

What’s your manifest (you may skip the icons)?

Thanks so much for the help!

Here’s a ZIP of the minimal plugin I tested:
:paperclip: [UXPMinimalPlugin.zip]

It includes only:

  • a manifest.json

  • a basic index.html with a <button>

  • one line of inline JavaScript that updates a <pre> tag when the button is clicked

No imports, no modules, no build tools — just raw HTML + JS.

Even this fails to display anything in the panel on my machine.
If anyone can try it on their side, I’d really appreciate knowing if it works — or if something in my environment might be blocking it.

Thanks again :folded_hands:
— Lior

Someone will no doubt correct me if I am wrong, but I don’t think you can have a plugin without a single javascript file.

If I take out these lines that you have in your manifest:

      "panel": {
        "mainPath": "index.html"
      }

And add this line at the top level:

  "main": "index.html",

Then it does get past the Uncaught Error: Cannot resolve module: main.js error, but throws this error:

Uncaught UxpRuntimeError: Refusing to load inline script tag as executable code. Code generation is disabled in plugins.

Which makes me think you can’t do inline scripting as you are trying to.

If I comment out the script tag that you have in your index.html file, then the plugin does load showing the panel and button . . . but, of course, the click of the button isn’t handled.

If I were setting this up, I would change the manifest as above and remove that script tag from the index.html file.

I would then create a javascript file in my folder. It can be named “main.js”, or ‘index.js” as @AnthonyK (above) does, or whatever.

It needs to be linked to from the index.html file, so in the head tag of the index.html file, goes the line @AnthonyK offered:

<script src="index.js"></script> 

In this index.js file (the primary javascript file) there are certain things you need. You need to require entrypoints and you need an entrypoints.setup() function as the first function. I would be inclined to copy the sample code from here:

https://developer.adobe.com/photoshop/uxp/2022/guides/uxp_guide/uxp-misc/manifest-v5/#entrypointssetup-using-promises

You’ll just need to modify it for your plugin. In particular, (1)

Change the first line from

import { entrypoints } from "uxp";

To:

const { entrypoints } = require("uxp");

(2) where it has

panels: {
        panelA:

You will have the id of your panel:

panels: {
        minimalTestPanel:

(3) You can take out the commands section from that sample code, the reference to panelB and invokeMenu and menuItems (unless you are planning to use the panel flyout menu).

Finally, hooking up the click event listener . . .

I would often add that in the plugin create lifecycle hook. So you could just add the code you currently have in your script tag there. Your entrypoints.setup() function would look like this:

entrypoints.setup({
    plugin: {
        create() {
            console.log("Plugin has been loaded, plugin.create has been triggered.");

    document.getElementById("btnTest").addEventListener("click", function() {
      document.getElementById("output").textContent = "✅ Plugin works!";
    });

        },

That will work, but I would normally separate out the function I am calling on click.

So my entrypoints.setup() function would look like this:

entrypoints.setup({
    plugin: {
        create() {
            console.log("Plugin has been loaded, plugin.create has been triggered.");

			document.getElementById("btnTest").addEventListener("click", handleClick);
			
        },

And at the bottom of my file I would have:

function handleClick(theEvent) {
	document.getElementById("output").textContent = "✅ Plugin works!";
}

Hope all that helps
Philip