{"record":{"id":"f37558de4d7ea727","repo":"flowable/flowable-engine","slug":"user-info-with-key-key-does-not-exists-for-us","errorCode":null,"errorMessage":"User info with key '${key}' does not exists for user '${userId}'.","messagePattern":"User info with key '(.+?)' does not exists for user '(.+?)'\\.","errorType":"http","errorClass":"FlowableObjectNotFoundException","httpStatus":404,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/identity/UserInfoResource.java","lineNumber":115,"sourceCode":"    })\n    @DeleteMapping(\"/identity/users/{userId}/info/{key}\")\n    @ResponseStatus(HttpStatus.NO_CONTENT)\n    public void deleteUserInfo(@ApiParam(name = \"userId\") @PathVariable(\"userId\") String userId, @ApiParam(name = \"key\") @PathVariable(\"key\") String key) {\n        User user = getUserFromRequest(userId);\n        \n        if (restApiInterceptor != null) {\n            restApiInterceptor.deleteUser(user);\n        }\n        \n        String validKey = getValidKeyFromRequest(user, key);\n\n        identityService.setUserInfo(user.getId(), validKey, null);\n    }\n\n    protected String getValidKeyFromRequest(User user, String key) {\n        String existingValue = identityService.getUserInfo(user.getId(), key);\n        if (existingValue == null) {\n            throw new FlowableObjectNotFoundException(\"User info with key '\" + key + \"' does not exists for user '\" + user.getId() + \"'.\", null);\n        }\n\n        return key;\n    }\n}\n","sourceCodeStart":97,"sourceCodeEnd":121,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/identity/UserInfoResource.java#L97-L121","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the key exists: GET identity/users/{userId}/info and confirm the key is in the list before accessing it.","Correct the key spelling/casing in the request URL.","If the entry may legitimately not exist, handle the 404 (FlowableObjectNotFoundException) response gracefully instead of treating it as a bug.","Create the entry first with PUT identity/users/{userId}/info/{key} with a non-null value, then read it."],"exampleFix":"// before (fails when key absent)\nGET /identity/users/jdoe/info/email\n\n// after — create or ensure it first\nPUT /identity/users/jdoe/info/email\n{\"value\":\"jdoe@example.com\"}\n// then\nGET /identity/users/jdoe/info/email","handlingStrategy":"try-catch","validationCode":"// list existing info entries first and check the key\nconst res = await fetch(`/identity/users/${userId}/info`);\nconst entries = await res.json();\nif (!entries.some(e => e.key === key)) {\n  // key does not exist — create it or skip\n}","typeGuard":"function infoKeyExists(entries, key) {\n  return Array.isArray(entries) && entries.some(e => e && e.key === key);\n}","tryCatchPattern":"try {\n  const res = await fetch(`/identity/users/${userId}/info/${key}`);\n  if (res.status === 404) {\n    // FlowableObjectNotFoundException: key does not exist for this user\n    return null; // treat as 'no value' rather than a failure\n  }\n  return await res.json();\n} catch (e) { /* network error */ }","preventionTips":["List all info entries (GET .../info) before addressing a specific key.","Treat 404 on a single info key as 'absent', not as a client bug.","Standardize key names/casing across environments.","Create entries idempotently (PUT) before reading them in setup scripts."],"tags":["rest","not-found","user-info","flowable"],"backgroundTag":"record-not-found","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}