flowable/flowable-engine · error · FlowableObjectNotFoundException

User info with key ' ' does not exists for user ' '.

Error message

User info with key '${key}' does not exists for user '${userId}'.

What it means

FlowableObjectNotFoundException thrown by getValidKeyFromRequest when identityService.getUserInfo(userId, key) returns null, i.e. no user info entry exists for the given user and key. All operations on /identity/users/{userId}/info/{key} (GET, PUT, DELETE) validate the key first, so the request is rejected before any mutation. Flowable treats a missing info entry as a not-found resource rather than an empty value.

Solutions

  1. Verify the key exists: GET identity/users/{userId}/info and confirm the key is in the list before accessing it.
  2. Correct the key spelling/casing in the request URL.
  3. If the entry may legitimately not exist, handle the 404 (FlowableObjectNotFoundException) response gracefully instead of treating it as a bug.
  4. Create the entry first with PUT identity/users/{userId}/info/{key} with a non-null value, then read it.

Example fix

// before (fails when key absent)
GET /identity/users/jdoe/info/email

// after — create or ensure it first
PUT /identity/users/jdoe/info/email
{"value":"jdoe@example.com"}
// then
GET /identity/users/jdoe/info/email
Defensive patterns

Strategy: try-catch

Validate before calling

// list existing info entries first and check the key
const res = await fetch(`/identity/users/${userId}/info`);
const entries = await res.json();
if (!entries.some(e => e.key === key)) {
  // key does not exist — create it or skip
}

Type guard

function infoKeyExists(entries, key) {
  return Array.isArray(entries) && entries.some(e => e && e.key === key);
}

Try / catch

try {
  const res = await fetch(`/identity/users/${userId}/info/${key}`);
  if (res.status === 404) {
    // FlowableObjectNotFoundException: key does not exist for this user
    return null; // treat as 'no value' rather than a failure
  }
  return await res.json();
} catch (e) { /* network error */ }

Prevention

When it happens

Trigger: GET/PUT/DELETE identity/users/{userId}/info/{key} where the user exists but identityService.getUserInfo(userId, key) has never been set for that key, or the entry was deleted earlier. Note: an info entry stored as an empty string may still return non-null; a truly absent key throws.

Common situations: Typos in the info key ('Email' vs 'email'); querying a key on the wrong userId; keys removed by another process or during user cleanup; environments (test vs prod) where the info entry was never created.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/f37558de4d7ea727. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/identity/UserInfoResource.java:115

    })
    @DeleteMapping("/identity/users/{userId}/info/{key}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteUserInfo(@ApiParam(name = "userId") @PathVariable("userId") String userId, @ApiParam(name = "key") @PathVariable("key") String key) {
        User user = getUserFromRequest(userId);
        
        if (restApiInterceptor != null) {
            restApiInterceptor.deleteUser(user);
        }
        
        String validKey = getValidKeyFromRequest(user, key);

        identityService.setUserInfo(user.getId(), validKey, null);
    }

    protected String getValidKeyFromRequest(User user, String key) {
        String existingValue = identityService.getUserInfo(user.getId(), key);
        if (existingValue == null) {
            throw new FlowableObjectNotFoundException("User info with key '" + key + "' does not exists for user '" + user.getId() + "'.", null);
        }

        return key;
    }
}

View on GitHub (pinned to d6d39ce1c6)