Skip to content

[Bug]: UserConfig throws UnknownKeyException after concurrent preference insert when both caches are loaded #64191

Description

@tomasdbs

⚠️ This issue respects the following points: ⚠️

  • This is not a troubleshooting question, general support matter, or webserver/proxy problem, but likely a bug (if unsure, ask the Community Help Forum).
  • This issue is not already reported on Github OR solved at the Community Help Forum (I've searched!).
  • I'm using a maintained major version of Nextcloud Server and tested against the latest patch level. (Supported major versions and current patch levels).
  • I agree to follow Nextcloud's Code of Conduct.
  • I've tried my best to provide clear reproduction steps that someone unfamiliar with this bug could use to reproduce it.

Bug description

I have seen this error intermittently for several users on Nextcloud 34.0.3.2. One example occurred during a background PUT /ocs/v2.php/apps/user_status/api/v1/heartbeat?format=json request. The stack goes through token-login completion and Activity's SetUserDefaults listener before failing in core UserConfig::setTypedValue():

unknown key activity configured for <redacted-user> even though insert raised a duplicate contraint

Activity is trying to save activity/configured=yes. Core handles the duplicate insert by calling loadConfigAll(), but that does not read the database again if both cache halves are already marked loaded. The newly inserted preference is still missing from valueDetails, so core throws UnknownKeyException. Activity does not catch the exception from its final configured write.

The heartbeat handler is not reached. The inspected OCS v2 error path returns HTTP 500; the supplied exception log does not itself record the HTTP response status. The exact overlapping production request has not been identified.

Steps to reproduce

This can be reproduced deterministically without timing two HTTP requests. In an isolated test fixture:

  1. Create two independent OC\Config\UserConfig instances, A and B, sharing the same preferences store. Start with no preferences for a synthetic user.
  2. Call B->getApps($uid) so both the fast and lazy caches are loaded while activity/configured is absent.
  3. Call A->setValueMixed($uid, "activity", "configured", "yes"); the insert succeeds.
  4. Call the same setter on B. It encounters a uniqueness conflict and throws the exception above instead of discovering the row.

Illustrative sequence (A and B must be separate instances, not two references to the same container service):

$uid = "synthetic-user";
$b->getApps($uid);
$a->setValueMixed($uid, "activity", "configured", "yes");
$b->setValueMixed($uid, "activity", "configured", "yes"); // throws

The standalone check executed the actual installed UserConfig and Activity listener with an in-memory database double modeling unique preference keys and minimal service doubles. It also exercised the final write through SetUserDefaults::handle(new PostLoginEvent(...)).

Results:

  • The exact exception escapes the Activity listener.
  • Failed duplicate recovery performs zero additional database reads.
  • One correct configured=yes row remains stored.
  • A fresh UserConfig instance sees the row and Activity skips initialization.
  • Control: if only the fast cache was loaded before A inserted, B performs the full reread and recovers.

The same assertions passed with the exact 34.0.4 UserConfig.php from commit 77c7284fed1f5c7b4bce1a94299e229f3281b606. This is method-level testing with a modeled database, not an end-to-end 34.0.4 installation, a real SQL concurrency test, or a claim that the production interleaving was captured.

Expected behavior

When another request has already inserted the same preference, core should reread the existing row and complete the normal update/no-op handling, preserving the stored type and flags. Saving an already-persisted activity/configured=yes marker should not abort login completion or an otherwise valid API request.

Nextcloud Server version

34

Operating system

Debian/Ubuntu

PHP engine version

PHP 8.5

Web server

Apache (supported)

Database engine version

MariaDB

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

None

Are you using the Nextcloud Server Encryption module?

None

What user-backends are you using?

  • Default user-backend (database)
  • LDAP/ Active Directory
  • SSO - SAML
  • Other

Configuration report

Relevant context only; full production config:list output is omitted.
Accounts: LDAP/Active Directory.
Browser authentication: automatic Windows/Kerberos SSO through Apache.
user_saml type: environment-variable
user_saml general-uid_mapping: REMOTE_USER
The isolated reproduction does not use LDAP, Kerberos, or user_saml.

List of activated Apps

Relevant apps, not a full app:list dump:
- activity (bundled app; installed test copy reports 7.0.0)
- user_status (the affected heartbeat endpoint)
- user_ldap
- user_saml 8.3.1
The error is thrown by core UserConfig during Activity preference initialization.

Nextcloud Signing status

Nextcloud Logs

Redacted, shortened exception excerpt; unrelated fields and repetitive dispatcher frames omitted:

Nextcloud: 34.0.3.2
Time: 2026-09-09T06:26:56+00:00
Level: 3
Method: PUT
URL: /ocs/v2.php/apps/user_status/api/v1/heartbeat?format=json
User: <redacted-user>
Exception: OCP\Config\Exceptions\UnknownKeyException
Message: unknown key activity configured for <redacted-user> even though insert raised a duplicate contraint
Thrown at: lib/private/Config/UserConfig.php:1227

Call chain (request entry to failing write):
ocs/v2.php -> ocs/v1.php:72
-> OC::handleLogin()
-> OC\User\Session::tryTokenLogin()
-> loginWithToken()
-> completeLogin()
-> PostLoginEvent
-> OCA\Activity\Listener\SetUserDefaults::handle():37
-> SetUserDefaults::setDefaultsForUser():69
-> OC\AllConfig::setUserValue():226
-> OC\Config\UserConfig::setValueMixed():920
-> UserConfig::setTypedValue():1227

Additional info

Versions: production Nextcloud 34.0.3 (internal 34.0.3.2), Apache 2.4.66, PHP-FPM 8.5.4, MariaDB 11.8; affected browser reported Edge 152 on Windows. The standalone check ran on PHP 8.5.4. Exact installation/upgrade history and server-side encryption status were not collected.

Latest patch check: 34.0.4 was released on September 10. Its UserConfig.php is byte-for-byte identical to the 34.0.3 file tested, and the reproduction was repeated against that exact 34.0.4 source.

Source references:

Related changes: #62586 / #63093 added the explicit exception and diagnostic reason, but did not force a cache refresh. Activity #2791 catches exceptions around individual notification-default writes; the final configured marker remains outside that catch.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    0. Needs triagePending check for reproducibility or if it fits our roadmap34-feedbackbug

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions