Skip to content

[Bug]: "Edit Locally" on already-locked file shows "Lock will last for 0 minutes" in release builds #9972

Description

@Lobster-0429

⚠️ Before submitting, please verify the following: ⚠️

Bug description

When the user invokes Edit Locally on a file that is already in LockStatus::LockedItem
state (for example, re-opening a file that was locked moments earlier in the same session,
or a file still under an active token lock), the resulting notification reads:

File <name> already locked.
Lock will last for 0 minutes.

The lock itself is valid and works correctly — only the displayed remaining time is wrong.
This happens on every release build, 100% reproducible.

Root cause

In src/gui/editlocallyjob.cpp, the
fileAlreadyLocked() method reads the current lock state through a Q_ASSERT:

void EditLocallyJob::fileAlreadyLocked()
{
    SyncJournalFileRecord rec;
    Q_ASSERT(_folderForFile->journalDb()->getFileRecord(_relativePathToRemoteRoot, &rec));
    Q_ASSERT(rec.isValid());
    Q_ASSERT(rec._lockstate._locked);

    const auto remainingTimeInMinutes = fileLockTimeRemainingMinutes(
        rec._lockstate._lockTime, rec._lockstate._lockTimeout);
    fileLockProcedureComplete(tr("File %1 already locked.").arg(_fileName),
                              tr("Lock will last for %1 minutes. ...")
                                  .arg(remainingTimeInMinutes),
                              true);
}

Q_ASSERT(x) expands to ((void)0) when QT_NO_DEBUG is defined — i.e. in every CMake
Release build. getFileRecord() (declared [[nodiscard]] bool at
src/common/syncjournaldb.h:55-56) is therefore
never called in release. rec stays default-constructed, so _lockTime = 0 and
_lockTimeout = 0, and fileLockTimeRemainingMinutes(0, 0) returns 0.

The project's own assert header src/common/asserts.h:61-65
explicitly documents this rule:

Q_ASSERT — An assert that is only present in debug builds: typically used for asserts
that are too expensive for release mode.

Wrapping a [[nodiscard]] side-effecting DB call in Q_ASSERT simultaneously silences
the nodiscard warning and drops the call in release — the worst possible combination.

Steps to reproduce

  1. Build the desktop client in Release mode (the shipped configuration).
  2. Connect to a Nextcloud server and sync any folder.
  3. Right-click an Office/text file in the synced folder and choose Edit Locally
    the file becomes locked, the editor opens.
  4. Without releasing the lock, close the editor and again right-click the same file →
    Edit Locally.
  5. Observe the system notification.

Expected behavior

The notification should read:

File <name> already locked.
Lock will last for N minutes. You can also unlock this file manually once you are
finished editing.

…where N is the actual remaining lock time in minutes (matching the fileLockSuccess
path at editlocallyjob.cpp:528-537).

Which files are affected by this bug

  • src/gui/editlocallyjob.cppEditLocallyJob::fileAlreadyLocked() (lines 514–526)

Operating system

Windows

Which version of the operating system you are running.

Windows (also reproduces on macOS and Linux — the bug is in cross-platform code, not platform-specific).

Package

Official Linux AppImage

Nextcloud Server version

Any (server-side behavior is irrelevant to this bug)

Nextcloud Desktop Client version

33.0.50daily (master, commit a4b6d416d) — bug exists in all prior releases that ship this code path.

Is this bug present after an update or on a fresh install?

Updated to a major version (ex. 3.16.3 to 3.17.0)

Are you using the Nextcloud Server Encryption module?

Encryption is Disabled

Are you using an external user-backend?

  • Default internal user-backend
  • LDAP/ Active Directory
  • SSO - SAML
  • Other

Nextcloud Server logs

N/A — purely a client-side display bug. No server interaction.

Additional info

  • This is a UX/messaging bug, not a data-integrity or security issue. The file lock
    is set and honored correctly by editlocallyjob.cpp:498-504.
  • Severity: low (cosmetic) but trivially fixable (~3 lines).
  • Related lint: a project-wide grep for Q_ASSERT\(.*\(.*\)\) would catch any other
    side-effecting calls hidden inside asserts.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions