Capacitor+Vite: can't build from CLI, can build in Android Studio but run gets "Uncaught TypeError: Failed to resolve module specifier "@capacitor/filesystem"."

I’m new to Capacitor and Vite, and plan to migrate my existing Cordova app over. I’m trying to run a trivial app but can’t-- as soon the JavaScript tries import { Filesystem, Directory, Encoding } from "@capacitor/filesystem"the Android studio log shows:

Uncaught TypeError: Failed to resolve module specifier "@capacitor/filesystem".

(I can’t build from the CLI-- running npx cap build android always errs with:

Gradle requires JVM 17 or later to run. Your build is currently configured to use JVM 8.

… even though I’ve configured everything to use JVM 21 everywhere I know about.)

I can’t create a new Capacitor project with npm init @capacitor/app@latest (reported here), so I’m trying to create a trivial app in Cordova then migrate it over to Capacitor.

Here’s the sequence of commands I’ve run:

# create and test demo Cordova app
cordova create test-cap-vite com.jmarshall.testcapvite TestCapacitorVite
cd test-cap-vite
cordova platform add android
cordova emulate android         # successfully build and run on emulator

# convert to Capacitor app
npm i @capacitor/core
npm i -D @capacitor/cli
npx cap init
  Name:  TestCapacitorVite
  Package ID:  com.jmarshall.testcapvite
  Web asset directory:  www
npm i @capacitor/android
npx cap add android
npm i @capacitor/filesystem

# edit files, shown below
vi www/index.html www/js/index.js

# install vite 
npm install -D vite

# build and sync app files-- is this correct?
npx vite build www
npx cap sync

# this fails with "Gradle requires JVM 17 or later to run. Your build is currently configured to use JVM 8."
npx cap build android

Here’s www/index.html:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
        <meta name="color-scheme" content="light dark">
        <link rel="stylesheet" href="css/index.css">
        <title>Capacitor + Vite test</title>
        <script type="module" src="js/index.js"></script>
    </head>
    <body>
        Capacitor + Vite test
    </body>
</html>

… and www/js/index.js:

import { Filesystem, Directory, Encoding } from "@capacitor/filesystem";

alert('starting Capacitor app') ;

let lib= await Filesystem.stat({
    directory: Directory.Library,
    path: '.',
}) ;

alert(lib) ;

Here’s capacitor.config.json:

{
  "appId": "com.jmarshall.testcapvite",
  "appName": "TestCapacitorVite",
  "webDir": "www"
}

If it matters, here’s package.json:

{
  "name": "com.jmarshall.testcapvite",
  "displayName": "TestCapacitorVite",
  "version": "1.0.0",
  "description": "A sample Apache Cordova application that responds to the deviceready event.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "ecosystem:cordova"
  ],
  "author": "Apache Cordova Team",
  "license": "Apache-2.0",
  "devDependencies": {
    "@capacitor/cli": "^8.4.2",
    "cordova-android": "^15.1.0",
    "vite": "^8.1.5"
  },
  "cordova": {
    "platforms": [
      "android"
    ]
  },
  "dependencies": {
    "@capacitor/android": "^8.4.2",
    "@capacitor/core": "^8.4.2",
    "@capacitor/filesystem": "^8.1.2"
  }
}

I can build it in Android Studio (after some Java version yak-shaving), but when it runs it fails on the import statement as described above. index.html loads correctly, but the alerts are never run.

Isn’t Vite suppose to resolve "@capacitor/filesystem"in its build? Capacitor requires a bundler, right?

This is all using the latest versions of almost everything, I believe:

  • MacOS 26.5.2 (M1 on Apple Silicon)
  • Android Studio Quail 2 | 2026.1.2 Patch 1
  • Android emulator for Pixel 7, API 36.1 (the only non-latest version here)
  • Capacitor 8.4.2
  • Vite 8.1.5

Any ideas why this is failing, and how to fix it? Or, how to investigate further?

Thank you so much for any help! :relieved_face:

If you go to File > Settings/Preferences > Build, Execution, Deployment > Build Tools > Gradle, what is your Gradle JDK set to?

Gradle JDK is:

“homebrew-26 (5) Homebrew OpenJDK 26.0.1 - aarch64 /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home”

The “(5)” is just because it’s the 5th “homebrew-36” option, as I was trying a few JDK versions.

I don’t know if this matters, but the “openjdk@21” path segment is a symbolic link that I manually created. It points to the same place as all the other “openjdk@xx” links that already existed-- “../Cellar/openjdk/26.0.1” . I don’t know if this is all that is needed to support JDK 21.

I’m supposed to be using JDK 21, right?

Thanks!

I believe 21 is correct. That is what I am using.

Have you tried setting JAVA_HOME? See Troubleshooting Android Issues | Capacitor Documentation.

I am assuming you have restarted since changing this setting? I’ve had the Gradle Daemon hang onto an old setting until it restarts.

Great, I’ll stick with JVM 21 then.

I tried a bunch of JAVA_HOME values and settled on /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home. I keep that synchronized with the Gradle SDK in the Android Studio settings, and restart AS each time I change it. I restart AS using npx cap open android to ensure AS can pick up the right JAVA_HOME from the shell.

I restarted AS a few times, but not the Mac. I just restarted my Mac too, but it didn’t help.

Between my last comment and this one, I updated to the brand new Android Studio, Quail 3. It asked to install the JVM 25 from JBR, and I (perhaps naively) let it. That wouldn’t let the Gradle sync, so I reverted manually to OpenJDK 21. This required manually editing node_modules/@capacitor/android/capacitor/build.gradle and node_modules/@capacitor/filesystem/android/build.gradle , changing android { compileOptions {...} } to both use Java 21.

Gradle syncs again, and it builds OK again, but I’m still getting the error resolving "@capacitor/filesystem"when it runs.

I found a potentially useful warning in the Android Studio logcat:

D  Starting BridgeActivity
D  Registering plugin instance: CapacitorCookies
D  Registering plugin instance: WebView
D  Registering plugin instance: CapacitorHttp
D  Registering plugin instance: SystemBars
D  Registering plugin instance: Filesystem
W  Unable to read file at path public/plugins

So something is trying to read public/plugins from somewhere in order to load the Filesystem plugin, but it can’t find it. I did install the Filesystem plugin, but maybe a directory didn’t get created? I installed it with:

npm i @capacitor/filesystem

The new info about public/plugins changes the focus of this thread, plus I’ve learned more from experimenting, so I wrote the whole thing up more clearly in a new topic here. (I can’t edit this topic, sorry.)

It looks like Vite isn’t actually bundling your app, so the browser is seeing @capacitor/filesystem as a bare import and doesn’t know how to resolve it.Normally Vite should process those imports before they ever reach the WebView.

One thing that stood out is that you’re using www as both your source folder and your output folder. Vite is usually set up with a separate source directory(like src) and then builds into www or dist. That might be why the import is staying untouched.

Have you tried creating a fresh Vite project first, confirming that it works with Capacitor, and then moving your Cordova code into it instead of converting the Cordova project directly?

Thanks for the reply, I appreciate it. Part of the problem is that I can’t create a fresh Capacitor project, as I get errors when I run npm init @capacitor/app@latest (as detailed here), so the only way I know how to create any Capacitor project is to convert a Cordova project. I actually would prefer to create a fresh Capacitor project and then copy my files into it. Does a Vite project need to be created before making it a Capacitor project?

Interesting about source vs. output folders. When running npx cap init, which does “Web asset directory” (or “webDir” in capacitor.config.json) refer to? I thought it would be the source, so I accepted the suggested answer of “www”, which is what Cordova uses under the project root. When I run npx vite build www, I’ve assumed the www there is also the source directory, since it won’t build if I run a bare npx vite build– it’s looking for index.html in the project root directory. Where is the output folder set?

How can I tell if Vite is bundling the app? It looks like the build command creates www/dist/, under which are a modified index.html and an assets/ directory with generated assets.

To build and run, should I indeed run npm vite build www&& npx cap sync, followed by either npx cap run android or a run in Android Studio? I couldn’t find a definitive answer for the right sequence.

Thanks a lot! :slight_smile: