romm icon indicating copy to clipboard operation
romm copied to clipboard

[Bug] Hasheous fails to identify ROMs when each game is organized in its own folder

Open zeedif opened this issue 2 months ago • 12 comments

RomM version 4.4.0-beta.3

Describe the bug I have organized my entire library so that every game, including single-file ROMs, is placed within its own dedicated folder. According to the documentation, this structure is supported and allows for adding related files like hacks, translations, and manuals in subfolders, which provides great flexibility.

However, this organizational choice breaks metadata identification when using the Hasheous provider. All games organized this way are reported as "Not Identified" by Hasheous.

To Reproduce Steps to reproduce the behavior:

  1. Organize a ROM library using 'Structure A' or 'Structure B' as described in the documentation.
  2. Inside a platform folder (e.g., /roms/neo-geo-pocket), create a directory for a game, for example, Baseball Stars - Pocket Sports Series (Japan, Europe) (En,Ja)/.
  3. Place the game files inside this directory. For example: Baseball Stars - Pocket Sports Series (Japan, Europe) (En,Ja).ngp.
  4. Run a scan with the Hasheous metadata provider enabled.
  5. Observe the log or the UI to see that the game is not identified by Hasheous.

Expected behavior RomM should send the hash of the file inside the folder and find a match, just like I can do manually (as shown in the screenshots below).

Screenshots Image

Image Image

Desktop (please complete the following information):

  • OS: Any (Backend issue)
  • Browser: Any
  • Version: Any

Smartphone (please complete the following information):

  • OS: Any (Backend issue)
  • Device: Any
  • Browser: Any
  • Version: Any

Additional context

INFO:     [RomM][websockets_impl][2025-11-09 17:37:09] 192.168.1.67:0 - "WebSocket /ws/socket.io/?EIO=4&transport=websocket" [accepted]
INFO:     [RomM][server][2025-11-09 17:37:09] connection open
INFO:     [RomM][scan][2025-11-09 17:37:09] 🔎 Scanning
17:37:09 high: endpoints.sockets.scan.scan_platforms(metadata_sources=['hasheous'], platform_ids=[73], roms_ids=[], scan_type=<ScanType.UNMATCHED: 'unmatched'>) (253ff86b-2cc0-4226-bc8c-491d9b8bcae6)
INFO:     [RomM][scan][2025-11-09 17:37:15] Found 1 platforms in the file system
INFO:     [RomM][scan][2025-11-09 17:37:15] Folder neo-geo-pocket[snk-neogeo-pocket] identified as Neo Geo Pocket 🎮
WARNING:  [RomM][scan][2025-11-09 17:37:15] ⚠️ No firmware found for Neo Geo Pocket[snk-neogeo-pocket]
INFO:     [RomM][scan][2025-11-09 17:37:15] 9 roms found in the file system
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:15] Baseball Stars - Pocket Sports Series (Japan, Europe) (En,Ja) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:15] King of Fighters R-1 - Pocket Fighting Series (Japan, Europe) (En,Ja) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:15] Melon-chan no Seichou Nikki (Japan) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:15] Neo Cherry Master - Real Casino Series (Japan) (En,Ja) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:15] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:15] NeoGeo Cup '98 (Japan, Europe) (En,Ja) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:16] Pocket Tennis - Pocket Sports Series (Japan, Europe) (En,Ja) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:16] Renketsu Puzzle Tsunagete Pon! (Japan) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:16] Samurai Shodown! - Pocket Fighting Series (Japan, Europe) (En,Ja) not identified ❌
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No IGDB ID provided for Hasheous IGDB game lookup.
INFO:     [RomM][hasheous_handler][2025-11-09 17:37:16] No RA ID provided for Hasheous RA game lookup.
WARNING:  [RomM][scan][2025-11-09 17:37:16] Shougi no Tatsujin (Japan) not identified ❌
WARNING:  [RomM][scan][2025-11-09 17:37:16] Missing platforms from filesystem:
WARNING:  [RomM][scan][2025-11-09 17:37:16]  - bios (bios)
INFO:     [RomM][scan][2025-11-09 17:37:16] ✔️ Scan completed
INFO:     [RomM][nginx][2025-11-09 17:37:16] 172.18.0.250 | 192.168.1.67 | GET /ws/socket.io/?EIO=4&transport=websocket 101 | 3072 | Chrome Windows | 7.127
INFO:     [RomM][server][2025-11-09 17:37:16] connection closed
17:37:16 Successfully completed endpoints.sockets.scan.scan_platforms(metadata_sources=['hasheous'], platform_ids=[73], roms_ids=[], scan_type=<ScanType.UNMATCHED: 'unmatched'>) job in 0:00:04.184081s on worker b87c34a5d91540e1a31210018cc0644f
17:37:16 high: Job OK (253ff86b-2cc0-4226-bc8c-491d9b8bcae6)
17:37:16 Result is kept for 86400 seconds

zeedif avatar Nov 09 '25 17:11 zeedif

Not sure what's up with your ROMs but on the latest beta I can match with a nested folder just fine (ignore the hasheous ID i'm using their beta).

Image

gantoine avatar Nov 09 '25 20:11 gantoine

Why does it look like your files don't have file extensions?

Spinnich avatar Nov 09 '25 21:11 Spinnich

My files do have extensions. The folder name is just the same as the filename, so ls hides the extension in the first command. Here's the content of one of the folders:

zeedif@zeepubs:~$ ls /mnt/hogar/Roms
1g1r.txt     arcade-mame  atari-8bit       atari-st       commodore-64-pp     commodore-amiga-cd32  microsoft-xbox        msx1             nec-pcengine-supergrafx  nintendo-64                nintendo-dsi          nintendo-gamecube      nintendo-gba-homebrew   nintendo-satellaview   nintendo-switch-2     nintendo-wii-ware  sega-cd                      sega-megadrive-genesis  snk-neogeo-cd            sony-ps2  sony-psvita
arcade-cps1  atari-2600   atari-jaguar     bios           commodore-64-tapes  commodore-plus4       microsoft-xbox360     msx2             nintendo-3ds             nintendo-64dd              nintendo-dsi-digital  nintendo-gb            nintendo-gba-multiboot  nintendo-snes          nintendo-virtual-boy  pokemon-mini       sega-dreamcast               sega-pico               snk-neogeo-pocket        sony-ps3
arcade-cps2  atari-5200   atari-jaguar-cd  coleco-vision  commodore-amiga     commodore-vic20       microsoft-xboxone     nec-pcengine     nintendo-3ds-digital     nintendo-ds                nintendo-dsvision     nintendo-gba           nintendo-gbc            nintendo-sufami-turbo  nintendo-wii          sega-32x           sega-gamegear                sega-saturn             snk-neogeo-pocket-color  sony-ps4
arcade-cps3  atari-7800   atari-lynx       commodore-64   commodore-amiga-cd  microsoft-dos         microsoft-xboxseries  nec-pcengine-cd  nintendo-3ds-new         nintendo-ds-download-play  nintendo-fds          nintendo-gba-e-reader  nintendo-nes            nintendo-switch        nintendo-wiiu         sega-beena         sega-master-system-mark-iii  sega-sg1000             sony-ps1                 sony-psp
zeedif@zeepubs:~$ ls /mnt/hogar/Roms/snk-neogeo-pocket
'Baseball Stars - Pocket Sports Series (Japan, Europe) (En,Ja)'          'Melon-chan no Seichou Nikki (Japan)'                     'NeoGeo Cup '\''98 (Japan, Europe) (En,Ja)'                     'Renketsu Puzzle Tsunagete Pon! (Japan)'                             'Shougi no Tatsujin (Japan)'
'King of Fighters R-1 - Pocket Fighting Series (Japan, Europe) (En,Ja)'  'Neo Cherry Master - Real Casino Series (Japan) (En,Ja)'  'Pocket Tennis - Pocket Sports Series (Japan, Europe) (En,Ja)'  'Samurai Shodown! - Pocket Fighting Series (Japan, Europe) (En,Ja)'
zeedif@zeepubs:~$ ls "/mnt/hogar/Roms/snk-neogeo-pocket/Baseball Stars - Pocket Sports Series (Japan, Europe) (En,Ja)"
'Baseball Stars - Pocket Sports Series (Japan, Europe) (En,Ja).ngp'

Just in case it helps, here are my config.yml and docker-compose.yml setups.

config.yml:

emulatorjs:
  cache_limit: null
  controls: {}
  debug: false
  settings: {}
exclude:
  platforms:
  - ".saves"
  - "bios"
  roms:
    multi_file:
      names: []
      parts:
        extensions:
        - md
        - txt
        - xml
        - rdb
        - json
        - dat
        names: []
    single_file:
      extensions:
      - md
      - txt
      - xml
      - rdb
      - json
      - dat
      names: []
filesystem:
  firmware_folder: bios
  roms_folder: .
scan:
  priority:
    artwork:
    - ss
    - igdb
    - hasheous
    language:
    - es
    - en
    metadata:
    - ss
    - igdb
    - hasheous
    region:
    - us
    - wor
    - ss
    - eu
    - jp
  media:
    - box2d
    - screenshot
system:
  platforms:
    atari-2600: atari2600
    atari-5200: atari5200
    atari-7800: atari7800
    atari-8bit: atari8bit
    atari-jaguar: jaguar
    atari-jaguar-cd: atari-jaguar-cd
    atari-lynx: lynx
    atari-st: atari-st
    coleco-vision: colecovision
    commodore-64: c64
    commodore-amiga: amiga
    commodore-amiga-cd32: amiga-cd32
    commodore-plus4: c-plus-4
    commodore-vic20: vic-20
    genesis-slash-megadrive: genesis
    microsoft-dos: dos
    microsoft-xbox: xbox
    microsoft-xbox360: xbox360
    microsoft-xboxone: xboxone
    microsoft-xboxseries: series-x
    nec-pcengine: tg16
    nec-pcengine-cd: turbografx-cd
    nec-pcengine-supergrafx: supergrafx
    nintendo-3ds: 3ds
    nintendo-3ds-new: new-nintendo-3ds
    nintendo-64: n64
    nintendo-64dd: 64dd
    nintendo-fds: fds
    nintendo-gamecube: ngc
    nintendo-gb: gb
    nintendo-gba: gba
    nintendo-gba-e-reader: e-reader
    nintendo-gbc: gbc
    nintendo-nes: nes
    nintendo-satellaview: satellaview
    nintendo-snes: snes
    nintendo-sufami-turbo: sufami-turbo
    nintendo-switch: switch
    nintendo-virtual-boy: virtualboy
    nintendo-wii: wii
    nintendo-wiiu: wiiu
    pokemon-mini: pokemon-mini
    sega-32x: sega32
    sega-beena: advanced-pico-beena
    sega-cd: segacd
    sega-dreamcast: dc
    sega-gamegear: gamegear
    sega-master-system-mark-iii: sms
    sega-megadrive-genesis: genesis
    sega-pico: sega-pico
    sega-saturn: saturn
    sega-sg1000: sg1000
    snk-neogeo-cd: neo-geo-cd
    snk-neogeo-pocket: neo-geo-pocket
    snk-neogeo-pocket-color: neo-geo-pocket-color
    sony-ps1: psx
    sony-ps2: ps2
    sony-ps3: ps3
    sony-ps4: ps4
    sony-psp: psp
    sony-psvita: psvita
  versions:
    arcade-cps1: arcade
    arcade-cps2: arcade
    arcade-cps3: arcade
    arcade-mame: arcade
    commodore-64-pp: c64
    commodore-64-tapes: c64
    commodore-amiga-cd: amiga
    msx1: msx
    msx2: msx2
    nintendo-3ds: 3ds
    nintendo-3ds-digital: 3ds
    nintendo-ds: nds
    nintendo-ds-download-play: nds
    nintendo-dsvision: nds
    nintendo-dsi: nintendo-dsi
    nintendo-dsi-digital: nintendo-dsi
    nintendo-gba-homebrew: gba
    nintendo-gba-multiboot: gba
    nintendo-wii-ware: wii

docker-compose.yml:

services:
  romm:
    image: ghcr.io/rommapp/romm:4.4.0-beta.3
    container_name: romm
    restart: unless-stopped
    volumes:
      - /mnt/hogar/Roms:/romm/library
      - ./romm/config:/romm/config
      - ./romm/resources:/romm/resources
      - ./romm/assets:/romm/assets
      - ./romm/redis:/redis-data
    environment:
      - DB_HOST=mariadb
      - DB_NAME=romm_db
      - DB_USER=mariadb
      - DB_PASSWD=[CENSORED]
      - ROMM_AUTH_SECRET_KEY=[CENSORED]
      - IGDB_CLIENT_ID=[CENSORED]
      - IGDB_CLIENT_SECRET=[CENSORED]
      - PLAYMATCH_API_ENABLED=true
      - HASHEOUS_API_ENABLED=true
      - STEAMGRIDDB_API_KEY=[CENSORED]
      - SCREENSCRAPER_USER=[CENSORED]
      - SCREENSCRAPER_PASSWORD=[CENSORED]
      - RETROACHIEVEMENTS_API_KEY=[CENSORED]
      - ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC=true
      - HLTB_API_ENABLED=true
      - OIDC_ENABLED=true
      - OIDC_PROVIDER=authelia
      - OIDC_CLIENT_ID=romm
      - OIDC_CLIENT_SECRET=[CENSORED]
      - OIDC_REDIRECT_URI=https://play.mydomain.com/api/oauth/openid
      - OIDC_SERVER_APPLICATION_URL=https://auth.mydomain.com
      - DISABLE_USERPASS_LOGIN=false
      - ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP=true
    depends_on:
      - mariadb
      - authelia

I'm on a clean install; I deleted my database and mounted files before starting with this beta version. After creating the administrator, RomM performs a general scan of all platforms and adds the games, apparently without any metadata source, simply appending the ROMs and their hashes. After noticing they had no cover art, I decided to use Hasheous and realized it wasn't finding any matches, regardless of the platform. I then tried with IGDB + Playmatch, and that at least works. The problem I'm having is specifically with Hasheous.

zeedif avatar Nov 09 '25 22:11 zeedif

After digging a bit more, the issue seems to occur with _should_get_rom_files in endpoints/sockets/scan.py. romm\backend\endpoints\sockets\scan.py

def _should_get_rom_files(
    scan_type: ScanType, rom: Rom, newly_added: bool, roms_ids: list[int]
) -> bool:
    """Decide whether a ROM's files should be rebuilt or not"""
    return bool(
        (scan_type in {ScanType.NEW_PLATFORMS, ScanType.QUICK} and newly_added)
        or (scan_type == ScanType.COMPLETE)
        or (scan_type == ScanType.HASHES)
        or (rom and rom.id in roms_ids)
    )

If this function returns False, the process appears to skip the call to fs_rom_handler.get_rom_files(rom), which is the function that fills the list of files and their hashes.

    # ... (inside _identify_rom)
    # Build rom files object before scanning
    should_update_files = _should_get_rom_files(
        scan_type=scan_type, rom=rom, newly_added=newly_added, roms_ids=roms_ids
    )
    if should_update_files:
        # This block is skipped, so get_rom_files() is never called
        log.debug(f"Calculating file hashes for {rom.fs_name}...")
        rom_files, rom_crc_c, rom_md5_h, rom_sha1_h, rom_ra_h = (
            await fs_rom_handler.get_rom_files(rom)
        )
        fs_rom.update(
            {
                "files": rom_files,
                "crc_hash": rom_crc_c,
                "md5_hash": rom_md5_h,
                "sha1_hash": rom_sha1_h,
                "ra_hash": rom_ra_h,
            }
        )

The fs_rom object passed to the main scan function (scan_rom in handler/scan_handler.py) does not contain the files list (fs_rom["files"]). Later, when meta_hasheous_handler.lookup_rom(...) is called, it receives an empty files list.

Inside lookup_rom in romm/backend/handler/metadata/hasheous_handler.py, the logic tries to find the largest file from the provided files list to get a hash, but because the list is empty it returns immediately without making an API call:

# romm\backend\handler\metadata\hasheous_handler.py

async def lookup_rom(self, platform_slug: str, files: list[RomFile]) -> HasheousRom:
    # ...
    # Select the largest file by size...
    first_file = max(filtered_files, key=lambda f: f.file_size_bytes, default=None)
    if first_file is None:
        return fallback_rom  # Returns here because 'files' is empty
    # ...

zeedif avatar Nov 11 '25 05:11 zeedif

@zeedif thanks for the thorough research! i think the first step would be to stop running scans on first startup of a new database, which i changed here: https://github.com/rommapp/romm/pull/2644

gantoine avatar Nov 11 '25 16:11 gantoine

@gantoine I updated to 4.4.0, deleted the database and all local RomM files. Then I started the container without mounting the library volume, letting the initial scans finish instantly. After that, I mounted the volume again and restarted the container.

With this clean setup, I ran a “Quick Scan” using only Hasheous. I confirmed that the scanner correctly detected the games, and the database shows that the hashes were indeed calculated and stored for each RomFile. However, they still weren’t identified.

Because of this, I think it may have to do with how file paths are being handled with my roms_folder: . configuration. I’m wondering if the is_top_level property in the RomFile model might be evaluating incorrectly due to this path structure, causing the lookup_rom function in the hasheous_handler to filter out the very files it should be checking.

Edit In my setup, due to the roms_folder: . configuration, the paths are constructed like this:

  • The Rom object's full_path becomes './<platform_folder>/<game_folder_name>'.
  • The RomFile object's file_path becomes '<platform_folder>/<game_folder_name>' (without the leading ./).

This might be causing the is_top_level property to be False for the main ROM file. As a result, when hasheous_handler.lookup_rom is called, it seems to filter out the very file it should be using for the hash lookup:

# D:\Workspace\romm\backend\handler\metadata\hasheous_handler.py

async def lookup_rom(self, platform_slug: str, files: list[RomFile]) -> HasheousRom:
    # ...
    filtered_files = [
        file
        for file in files
        if file.file_size_bytes > 0
        and file.is_top_level  # This check fails due to the path mismatch
        # ...
    ]

    first_file = max(filtered_files, key=lambda f: f.file_size_bytes, default=None)
    
    if first_file is None:
        return fallback_rom # The function exits here because filtered_files is empty.

zeedif avatar Nov 11 '25 16:11 zeedif

aah i see now, roms_folder should be a folder name, not .. you can leave it empty if the folder is called roms, or change it otherwise

gantoine avatar Nov 11 '25 16:11 gantoine

I used a dot because I wanted to avoid creating a separate 'roms' folder and just put the 'bios' folder in the same directory. I saw that 'rom' solved the problem and I only needed to exclude the 'bios' folder so it wouldn't be analyzed as a platform.

zeedif@zeepubs:~$ ls /mnt/hogar/Roms
arcade-mame  atari-8bit       atari-st      atari-2600   atari-jaguar     bios

I thought it was something that could be supported since I hadn't noticed any problems until now, and I think it would be useful to support it for those who don't want a structure like mine. I'll try leaving the entry blank to see if that maintains this style without causing this issue, and if not, I suppose I'll remap the ROMs volume and move the BIOS to another path.

Edit No work empty string:

INFO:     [RomM][init][2025-11-11 16:42:51] Starting internal valkey
INFO:     [RomM][init][2025-11-11 16:42:51] Running database migrations
CRITICAL: [RomM][config_manager][2025-11-11 16:42:54] Invalid config.yml: filesystem.roms_folder cannot be an empty string
ERROR:    [RomM][init][2025-11-11 16:42:54] Failed to run database migrations
INFO:     [RomM][init][2025-11-11 16:42:54] Stopping valkey-server

zeedif avatar Nov 11 '25 16:11 zeedif

remove the entire line from config.yml, we have fallbacks internally for everything in config.yml

gantoine avatar Nov 11 '25 16:11 gantoine

So, just to clarify, if I remove the roms_folder line entirely, will it default to looking for a roms folder inside my library path? After removing the line something went wrong:

INFO:     [RomM][scan][2025-11-11 16:48:56] 🔎 Scanning
16:48:57 high: endpoints.sockets.scan.scan_platforms(metadata_sources=['hasheous'], platform_ids=[5], roms_ids=[], scan_type=<ScanType.QUICK: 'quick'>) (fd99d741-0710-4cca-b22d-7c75ed8a80f4)
16:49:00 [Job fd99d741-0710-4cca-b22d-7c75ed8a80f4]: exception raised while executing (endpoints.sockets.scan.scan_platforms)
Traceback (most recent call last):
  File "/backend/handler/filesystem/roms_handler.py", line 535, in get_roms
    fs_single_roms = await self.list_files(path=rel_roms_path)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/backend/handler/filesystem/base_handler.py", line 532, in list_files
    raise FileNotFoundError(f"Directory not found: {full_path}")
FileNotFoundError: Directory not found: /romm/library/atari-lynx/roms

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/src/.venv/lib/python3.13/site-packages/rq/worker.py", line 1643, in perform_job
    return_value = job.perform()
  File "/src/.venv/lib/python3.13/site-packages/rq/job.py", line 1359, in perform
    self._result = self._execute()
                   ~~~~~~~~~~~~~^^
  File "/src/.venv/lib/python3.13/site-packages/rq/job.py", line 1396, in _execute
    coro_result = loop.run_until_complete(result)
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "/usr/local/lib/python3.13/contextlib.py", line 101, in inner
    return await func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/backend/endpoints/sockets/scan.py", line 589, in scan_platforms
    fs_roms = await fs_rom_handler.get_roms(Platform(fs_slug=platform_slug))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/backend/handler/filesystem/roms_handler.py", line 538, in get_roms
    raise RomsNotFoundException(platform=platform.fs_slug) from e
exceptions.fs_exceptions.RomsNotFoundException: Roms not found for platform atari-lynx. Check RomM folder structure here: https://github.com/rommapp/romm?tab=readme-ov-file#folder-structure

INFO:     [RomM][nginx][2025-11-11 16:49:10] 172.18.0.250 | 192.168.1.67 | GET /assets/LibraryManagement-Cey4o7KS.js 200 | 6267 | Chrome Windows | 0.000

My goal was to have a flatter structure, like /Roms/[platform_folders] and /Roms/bios all at the same level. I wanted to avoid having an extra /Roms/roms/ directory and didn't want to move my bios folder out to the root of the volume.

Using roms_folder: . was my workaround to achieve this, and it almost worked perfectly, except for this subtle pathing bug it seems to introduce.

For now, I'll go ahead and restructure my folders to what RomM expects (putting all platform folders inside a roms directory). I'll report back once I've done that and re-scanned. I'm pretty sure that will solve the immediate issue with Hasheous.

zeedif avatar Nov 11 '25 16:11 zeedif

change your library mount to - /mnt/hogar/Roms:/romm/library/roms

gantoine avatar Nov 11 '25 17:11 gantoine

I still insist, I understand that the path is directly concatenated and that's what was failing with Hasheous because I was using a dot (.) to keep all platforms at the root of the library, and I would really like that to be supported.

I've already made the suggested mount change and it works now. But understanding the issue, I adjusted my config to:

  firmware_folder: roms/bios
  roms_folder: roms

I hope this allows me to keep the style of having platforms and bios under the same folder inside the /roms directory.

If my proposal to support the flat structure isn't adopted, you should consider preventing . from being a valid value for the folder path, as someone else might run into the exact same problem.

zeedif avatar Nov 11 '25 17:11 zeedif