node.jsvue.jsnuxt.jsvuetify.jsnitro

Nuxt 4 build in production mode is trying to access public folder from .output/server/chunks/public instead of .output/public


I have a nuxt 4 app, with vuetify and sidebase nuxt auth. It runs normally during development, but when i build it and serve it using node .output/server/index.mjs, the page does not load, i can see errors accessing the files. for example:

H3Error: ENOENT: no such file or directory, open '/home/team/nuxt_app/.output/server/chunks/public/_nuxt/entry.D_Li3V-N.css' at async open (node:internal/fs/promises:641:25) ... 2 lines matching cause stack trace ... at async Server.toNodeHandle (file:///home/team/nuxt_app/.output/server/chunks/nitro/nitro.mjs:2316:7) { cause: Error: ENOENT: no such file or directory, open '/home/team/nuxt_app/.output/server/chunks/public/_nuxt/entry.D_Li3V-N.css' at async open (node:internal/fs/promises:641:25) at async Object.readFile (node:internal/fs/promises:1245:14) at async Object.handler (file:///home/team/nuxt_app/.output/server/chunks/nitro/nitro.mjs:2045:19) at async Server.toNodeHandle (file:///home/team/nuxt_app/.output/server/chunks/nitro/nitro.mjs:2316:7) { errno: -2, code: 'ENOENT', syscall: 'open', path: '/home/team/nuxt_app/.output/server/chunks/public/_nuxt/entry.D_Li3V-N.css' }, statusCode: 500, fatal: false, unhandled: true, statusMessage: undefined, data: undefined }

The file entry.D_Li3V-N.css exists on the path /home/team/nuxt_app/.output/public/_nuxt/

But for some reason the build is trying to access it from the server/chunks folder

This is happening for all files in the public folder.

Here is my nuxt.config.ts

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  compatibilityDate: '2024-11-01',
  devtools: { enabled: true },
  future: { compatibilityVersion: 4 },
  modules: [
    '@nuxt/eslint',
    '@nuxt/fonts',
    '@nuxt/icon',
    '@nuxt/image',
    '@nuxt/scripts',
    '@nuxt/test-utils/module',
    'vuetify-nuxt-module',
    '@sidebase/nuxt-auth',
    '@pinia/nuxt',
    'nuxt-mdi'
  ],

  ssr: false,

  // when enabling ssr option you need to disable inlineStyles and maybe devLogs
  features: {
     inlineStyles: false,
     devLogs: false,
  },

  build: {
     transpile: ['vuetify'],
  },

  vite: {
    ssr: {
      noExternal: ['vuetify'],
    },
  },

  css: ["~/assets/base/scss/style.scss"],

  auth: {
    provider: {
      type: 'authjs',
    },
  },
  devServer: { // Use 'server' for older Nuxt versions
    https: {
        key: './keys/server.key',
        cert: './keys/server.crt'
    }
  },

runtimeConfig:
{


    SESSION_TIMEOUT_SECS: process.env.SESSION_TIMEOUT_SECS,

    AUTH_SECRET: process.env.AUTH_SECRET,
    AUTH_ORIGIN: process.env.AUTH_ORIGIN,

    CERTBOT_DOMAIN: process.env.CERTBOT_DOMAIN,
    CERTBOT_EMAIL: process.env.CERTBOT_EMAIL,


    public:
        {
        }
},
  vuetify: {
    moduleOptions: {
      // check https://nuxt.vuetifyjs.com/guide/server-side-rendering.html
      ssrClientHints: {
        reloadOnFirstRequest: false,
        viewportSize: false,
        prefersColorScheme: false,

        prefersColorSchemeOptions: {
          useBrowserThemeOnly: false,
        },
      },

      styles: {
        configFile: 'assets/settings.scss',
      },
    },
  },
  nitro: {
      publicAssets: [
          {
              baseURL: '/',  // Serve at root (e.g., /myfile.jpg)
              dir: './public',  // Or your mounted volume path, e.g., '/app/extra-  public'
          },
      ],
      serveStatic: true,
  },
})

Solution

  • After trying many different things, I still do not know the exact reason this is happening. However, I made some changes to my code and the problem disappeared.

    There was a class in my code which was importing many other classes, which in turn used many third-party service packages. It was implementing a factory pattern to create clients for each service. Moving the import statements from the top level into the code solved the problem.

    E.g., I had:

    import {LocalFileSystemManager} from "\~\~/server/lib/LocalFileSystemManager"
    

    I replaced it with a function:

    async filestore(path: string): FileSystemManager
    {
        const runtime_config = useRuntimeConfig();
    
        const {LocalFileSystemManager}: typeof import("~~/server/lib/LocalFileSystemManager") = await import("~~/server/lib/LocalFileSystemManager");
    
        return new LocalFileSystemManager(path, this);
    }