{"record":{"id":"0b99c9f62dce5636","repo":"jackwener/OpenCLI","slug":"jike-notifications-api-returned-a-malformed-users","errorCode":null,"errorMessage":"Jike notifications API returned a malformed users list","messagePattern":"Jike notifications API returned a malformed users list","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/jike/notifications.js","lineNumber":60,"sourceCode":"}\n\nfunction cleanContent(value) {\n    return typeof value === 'string' ? value.replace(/\\n/g, ' ').slice(0, 100) : '';\n}\n\nfunction mapNotification(notification) {\n    if (!notification || typeof notification !== 'object' || typeof notification.id !== 'string' || !notification.id) {\n        throw new CommandExecutionError('Jike notifications API returned a malformed notification');\n    }\n    if (typeof notification.type !== 'string' || !notification.type) {\n        throw new CommandExecutionError('Jike notifications API returned a notification without a type');\n    }\n    const actionItem = notification.actionItem;\n    if (!actionItem || typeof actionItem !== 'object' || Array.isArray(actionItem)) {\n        throw new CommandExecutionError('Jike notifications API returned a malformed action item');\n    }\n    if (!Array.isArray(actionItem.users)) {\n        throw new CommandExecutionError('Jike notifications API returned a malformed users list');\n    }\n    const names = actionItem.users.map((user) => {\n        if (!user || typeof user !== 'object') {\n            throw new CommandExecutionError('Jike notifications API returned a malformed user');\n        }\n        return typeof user.screenName === 'string' ? user.screenName : '';\n    }).filter(Boolean);\n    const referenceItem = notification.referenceItem;\n    const referenceContent = referenceItem && typeof referenceItem === 'object' && !Array.isArray(referenceItem)\n        ? referenceItem.content\n        : '';\n    const time = typeof notification.createdAt === 'string'\n        ? notification.createdAt\n        : (typeof notification.updatedAt === 'string' ? notification.updatedAt : '');\n    return {\n        type: resolveActionLabel(notification, actionItem),\n        user: names.join('、'),\n        content: cleanContent(actionItem.content || referenceContent),","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/jike/notifications.js#L42-L78","documentation":"CommandExecutionError thrown by mapNotification when a notification's actionItem.users field is not an array. users holds the actors (with screenName) for the notification action; the code maps over it to build the 'user' column, so a missing or non-array users list makes the row impossible to produce and the library aborts with this error.","triggerScenarios":"Calling the jike notifications command when an item has valid id/type/actionItem object but actionItem.users is undefined, null, or a non-array — e.g. aggregated notifications changing shape or single-actor notifications using a scalar user field.","commonSituations":"Jike API rollout switching aggregated notifications to a different users representation; batched notifications omitting users; new notification category with a singular actor field; partial payloads during pagination.","solutions":["Capture the raw notification to confirm the users field shape in the new payload","Retry — determine if only specific notification types (aggregations) trigger it and filter those out","Patch mapNotification to default users to an empty array: `const users = Array.isArray(actionItem.users) ? actionItem.users : [];`","If a singular actor field exists, normalize it: wrap a non-array user object into [user] before mapping","Update TYPE_LABELS/mapping for the new aggregated-notification shape upstream"],"exampleFix":"// before\nif (!Array.isArray(actionItem.users)) {\n  throw new CommandExecutionError('Jike notifications API returned a malformed users list');\n}\n// after: normalize instead of throwing\nconst users = Array.isArray(actionItem.users)\n  ? actionItem.users\n  : (actionItem.users && typeof actionItem.users === 'object' ? [actionItem.users] : []);","handlingStrategy":"type-guard","validationCode":"function hasUsersList(n) {\n  return Array.isArray(n?.actionItem?.users);\n}\nconst usable = body.data.filter(n => hasNotificationId(n) && hasNotificationType(n) && hasActionItem(n) && hasUsersList(n));","typeGuard":"function hasValidUsersList(n) {\n  return Array.isArray(n?.actionItem?.users) &&\n    n.actionItem.users.every(u => u != null && typeof u === 'object');\n}","tryCatchPattern":"try {\n  rows = await jikeNotifications(page, limit);\n} catch (e) {\n  if (/malformed users list$/.test(e.message)) {\n    rows = (await fetchRaw()).map(n => {\n      const a = n.actionItem;\n      if (a && typeof a === 'object' && !Array.isArray(a) && !Array.isArray(a.users)) {\n        return { ...n, actionItem: { ...a, users: a.users ? [a.users] : [] } };\n      }\n      return n;\n    });\n  } else throw e;\n}","preventionTips":["Normalize actionItem.users to an array (wrap singular actor objects) before mapping","Validate every users element is an object with a screenName string","Keep a sample-based contract test on the notifications payload","Filter out aggregated-notification shapes your version does not support"],"tags":["api-contract","schema-validation","jike","notifications"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}