Skip to content

PHP 8 crash in GroupFolders trashbin when item has no extension (Undefined array key "extension") #4359

Open

Description

@fajarlabs-script

Steps to reproduce

  1. Create a Group Folder (Team folder)
  2. Inside the Group Folder:
    • Create a directory or a file without extension (e.g. README)
  3. Delete the directory / file
  4. Open the Trashbin via web UI or via WebDAV (PROPFIND on /remote.php/dav/trashbin/*)

Expected behaviour

Group Folder trashbin should open normally and list all deleted items,
including directories and files without extensions.

Actual behaviour

The Group Folder trashbin fails to load and returns a PHP fatal error
when the trash contains directories or files without an extension.

Server configuration

Operating system: Linux
Web server: Apache
Database: PostgreSQL
PHP version: PHP 8.3
Nextcloud version: 32.0.5
Team folders version: groupfolders (shipped / custom_apps)
Updated from an older Nextcloud/ownCloud or fresh install: fresh install
Where did you install Nextcloud from: official Nextcloud package
Are you using external storage, if yes which one: local
Are you using encryption: no
Are you using an external user-backend, if yes which one: no

Client configuration

Browser: any (also reproducible via WebDAV)
Operating system: Linux

Logs

Web server error log

Web server error log PHP Notice: Undefined array key "extension" in custom_apps/groupfolders/lib/Trash/TrashBackend.php on line ~451

PHP Fatal error: Uncaught TypeError:
substr(): Argument #1 ($string) must be of type string, null given

Nextcloud log (data/nextcloud.log)

Nextcloud log

Error during WebDAV PROPFIND /remote.php/dav/trashbin/*

Undefined array key "extension"
substr(): Argument #1 ($string) must be of type string, null given

Stack trace points to:
custom_apps/groupfolders/lib/Trash/TrashBackend.php:~451

Browser log

Browser log

PROPFIND /remote.php/dav/trashbin/* → 500 Internal Server Error

Additional information

  • The issue only occurs when the Group Folder trash contains:
    • directories (mimetype = httpd/unix-directory), or
    • files without a file extension
  • Database integrity is OK (no NULL values in name, original_location, or file_id)
  • Removing folder entries from oc_group_folders_trash immediately resolves the issue
  • Root cause appears to be assuming pathinfo()['extension'] always exists
  • pathinfo() does not return an extension key for directories or filenames without a dot
  • This leads to a PHP 8 fatal error when the value is later passed to substr()

A small defensive fix using $pathInfo['extension'] ?? '' and
$pathInfo['filename'] prevents the crash without changing behavior
for files that do have extensions.

Activity

  1. provokateurin commented on Feb 8, 2026

    @provokateurin
    Member

    Makes sense 👍 This even got caught in #4344, but I'll submit a separate fix tomorrow.

  2. self-assigned this
    on Feb 8, 2026
  3. added
    1. to developIssues that are ready for development
    and removed
    0. Needs triageIssues that need to be triaged
    on Feb 8, 2026
  4. fajarlabs-script commented on Feb 8, 2026

    @fajarlabs-script
    Author

    Makes sense 👍 This even got caught in #4344, but I'll submit a separate fix tomorrow.

    Thanks! I really appreciate it. Happy to test the fix once it's available.

  5. provokateurin commented on Feb 9, 2026

    @provokateurin
    Member

    I had a look, but couldn't reproduce this. Also the extension in this case is not the actual file extension but something we append manually. However if this really happens, then there must be a case where this extension isn't added properly.

  6. fajarlabs-script commented on Feb 9, 2026

    @fajarlabs-script
    Author

    I had a look, but couldn't reproduce this. Also the extension in this case is not the actual file extension but something we append manually. However if this really happens, then there must be a case where this extension isn't added properly.

    Thanks for checking.

    The issue we hit was in the trash name parsing logic, specifically in TrashBackend.php (around line 434).

    The previous implementation assumed the trash filename format was always valid and used pathinfo() + substr() to derive the extension and timestamp. This breaks when encountering anomalous names (e.g. file.txt.dabc, file.txt without a valid .d suffix), which then get parsed into an incorrect timestamp (often 0) and inconsistent keys.

    The local fix switches to:

    locating the .d separator using strrpos()

    validating the timestamp part using ctype_digit()

    skipping invalid entries (return null + array_filter) instead of force-parsing

    This prevents malformed trash items from polluting the trash list and avoids incorrect restore paths or metadata.

    With this in place the issue is no longer reproducible for us. I’ll try to isolate a minimal reproducible case if we can trigger it without malformed trash entries.

  7. provokateurin commented on Feb 9, 2026

    @provokateurin
    Member

    This sounds a bit like you used AI to analyze the source code. I don't see a PR by you that fixes this. Either way, the source of the issue has to be fixed, while we can also put a mitigation in place such that these malformed entries are not crashing the trashbin.

  8. fajarlabs-script commented on Feb 13, 2026

    @fajarlabs-script
    Author

    Thanks for looking into this.

    Just to clarify: yes, I applied a temporary defensive patch locally (basically checking for a missing extension before calling substr()), and that resolves the crash under PHP 8 in my environment.

    However, this is only a hot-fix. Every time the Groupfolders app is upgraded, the patch is overwritten. Since we rely heavily on Groupfolders in production, this effectively blocks us from upgrading safely.

    I don’t have a strong coding background, so I used AI assistance to identify the problematic line and apply a minimal defensive fix. It works, but of course I understand this is not a proper long-term solution.

    My hope is that this can be fixed upstream properly so upgrades won’t revert the workaround and reintroduce the crash.

    If it helps, I can share the exact patch I’m using for reference.

    Thanks again for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

1. to developIssues that are ready for developmentbug

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions