{"record":{"id":"fb5eb5f258e427c8","repo":"jackwener/OpenCLI","slug":"jike-notifications-api-returned-a-malformed-notifi","errorCode":null,"errorMessage":"Jike notifications API returned a malformed notification","messagePattern":"Jike notifications API returned a malformed notification","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/jike/notifications.js","lineNumber":50,"sourceCode":"    if (typeof TYPE_LABELS[notification.type] === 'string') return TYPE_LABELS[notification.type];\n    if (typeof actionItem.behavior === 'string' && actionItem.behavior.trim()) return actionItem.behavior.trim();\n    const sourceType = String(actionItem.type || notification.actionType || notification.type || '').toUpperCase();\n    if (sourceType.includes('LIKE')) return '赞了你';\n    if (sourceType.includes('COMMENT')) return '评论了你';\n    if (sourceType.includes('FOLLOW')) return '关注了你';\n    if (sourceType.includes('REPOST')) return '转发了你';\n    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;","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/jike/notifications.js#L32-L68","documentation":"CommandExecutionError thrown by mapNotification in clis/jike/notifications.js when a notification object from the Jike notifications API lacks a valid id. mapNotification requires each item to be a non-null object with a non-empty string id before it can be mapped into an output row; anything else is treated as a malformed API response rather than being silently skipped.","triggerScenarios":"Calling the jike notifications command when the /1.0/notifications/list response contains an entry that is null, not an object, or has a missing/empty/non-string id field.","commonSituations":"Jike API contract change introducing new item shapes (e.g. deleted-content placeholders with no id); undocumented item types in the feed; partial/edge-case notifications (recalled content) serialized differently; API returning test/error entries inside data array.","solutions":["Retry the command — if transient, a fresh fetch may return well-formed items","Reduce --limit or paginate differently to see whether a specific page contains the malformed item","Capture the raw /1.0/notifications/list response body and inspect the offending entry's shape","If Jike changed the contract, relax the check in mapNotification to skip items without id (filter) instead of throwing, or map the new shape","Report/patch: replace the throw with `if (!valid) return null;` and filter nulls in listNotifications"],"exampleFix":"// before\nif (!notification || typeof notification !== 'object' || typeof notification.id !== 'string' || !notification.id) {\n  throw new CommandExecutionError('Jike notifications API returned a malformed notification');\n}\n// after: skip malformed entries instead of failing the whole command\nfunction mapNotification(notification) {\n  if (!notification || typeof notification !== 'object' || typeof notification.id !== 'string' || !notification.id) {\n    return null;\n  }\n  ...\n}\nconst rows = body.data.map(mapNotification).filter(Boolean);","handlingStrategy":"type-guard","validationCode":"function isValidNotification(n) {\n  return n != null && typeof n === 'object' && typeof n.id === 'string' && n.id.length > 0;\n}\nconst usable = body.data.filter(isValidNotification); // pre-filter before mapping","typeGuard":"function hasNotificationId(n) {\n  return typeof n === 'object' && n !== null && typeof n.id === 'string' && n.id !== '';\n}","tryCatchPattern":"try {\n  rows = await jikeNotifications(page, limit);\n} catch (e) {\n  if (/malformed notification$/.test(e.message)) {\n    // refetch once; if it persists, the API contract changed — fall back to raw items\n    rows = (await fetchRaw()).filter(hasNotificationId);\n  } else throw e;\n}","preventionTips":["Filter raw API items through an id validity check before mapping","Pin and monitor the Jike notifications API response shape in tests","Log and skip (rather than crash on) single malformed items in your own wrappers","Watch for Jike API changelog updates that introduce new item kinds"],"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"}