{"record":{"id":"5c7baf1183870918","repo":"jackwener/OpenCLI","slug":"jike-notifications-api-returned-a-malformed-action","errorCode":null,"errorMessage":"Jike notifications API returned a malformed action item","messagePattern":"Jike notifications API returned a malformed action item","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/jike/notifications.js","lineNumber":57,"sourceCode":"    if (sourceType.includes('MENTION')) return '提到了你';\n    if (sourceType.includes('REPLY')) return '回复了你';\n    return notification.type;\n}\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 {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/jike/notifications.js#L39-L75","documentation":"CommandExecutionError thrown by mapNotification when a notification's actionItem field is missing, not an object, or is an array. actionItem carries the acting users and behavior content for the notification; without a valid object the row cannot be built, so the library treats the response as malformed and aborts the command.","triggerScenarios":"Calling the jike notifications command when an item from /1.0/notifications/list has id and type but actionItem is null/undefined/an array — e.g. notifications referencing deleted content or new notification categories with a different payload layout.","commonSituations":"Jike API change relocating action data to a new field; deleted/recalled action content causing null actionItem; system notifications without an action payload; mixed old/new payload versions during API rollout.","solutions":["Capture the raw notification JSON to see what replaced or nulled actionItem","Retry the command — if only notifications about deleted content trigger it, they may disappear from the feed over time","Patch mapNotification to skip such items (return null and filter) or to fall back to an alternate field (e.g. referenceItem) when actionItem is absent","Check whether a new notification type correlates with the missing actionItem and add a dedicated mapping branch","Update TYPE_LABELS/handling for the new payload shape so the row can be constructed"],"exampleFix":"// before\nif (!actionItem || typeof actionItem !== 'object' || Array.isArray(actionItem)) {\n  throw new CommandExecutionError('Jike notifications API returned a malformed action item');\n}\n// after: skip instead of failing\nif (!actionItem || typeof actionItem !== 'object' || Array.isArray(actionItem)) {\n  return null; // filtered by caller\n}","handlingStrategy":"type-guard","validationCode":"function hasActionItem(n) {\n  const a = n?.actionItem;\n  return a != null && typeof a === 'object' && !Array.isArray(a);\n}\nconst usable = body.data.filter(n => hasNotificationId(n) && hasNotificationType(n) && hasActionItem(n));","typeGuard":"function hasValidActionItem(n) {\n  return typeof n === 'object' && n !== null &&\n    n.actionItem !== null && typeof n.actionItem === 'object' && !Array.isArray(n.actionItem);\n}","tryCatchPattern":"try {\n  rows = await jikeNotifications(page, limit);\n} catch (e) {\n  if (/malformed action item$/.test(e.message)) {\n    rows = (await fetchRaw()).filter(n => n.actionItem && typeof n.actionItem === 'object' && !Array.isArray(n.actionItem));\n  } else throw e;\n}","preventionTips":["Pre-validate actionItem presence/shape before calling the mapper","Skip notifications about deleted content rather than failing the whole batch","Normalize alternate payload fields (e.g. a new action container) before mapping","Capture raw payloads when the error appears to detect API contract drift early"],"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"}