{"record":{"id":"3881caf6d467d29d","repo":"langgenius/dify","slug":"rating-must-be-either-like-or-dislike","errorCode":null,"errorMessage":"rating must be either 'like' or 'dislike'","messagePattern":"rating must be either 'like' or 'dislike'","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"sdks/nodejs-client/src/client/validation.ts","lineNumber":80,"sourceCode":"    if (typeof item !== 'string' || item.trim().length === 0) {\n      throw new ValidationError(`${name} must contain non-empty strings`)\n    }\n  })\n}\n\nexport function ensureOptionalStringArray(value: unknown, name: string): void {\n  if (value === undefined || value === null) {\n    return\n  }\n  ensureStringArray(value, name)\n}\n\nexport function ensureRating(value: unknown): void {\n  if (value === undefined || value === null) {\n    return\n  }\n  if (value !== 'like' && value !== 'dislike') {\n    throw new ValidationError(\"rating must be either 'like' or 'dislike'\")\n  }\n}\n\nexport function validateParams(params: Record<string, unknown>): void {\n  Object.entries(params).forEach(([key, value]) => {\n    if (value === undefined || value === null) {\n      return\n    }\n\n    // Only check max length for strings; empty strings are allowed for optional params\n    // Required fields are validated at method level via ensureNonEmptyString\n    if (typeof value === 'string') {\n      if (value.length > MAX_STRING_LENGTH) {\n        throw new ValidationError(\n          `Parameter '${key}' exceeds maximum length of ${MAX_STRING_LENGTH} characters`,\n        )\n      }\n    } else if (Array.isArray(value)) {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/sdks/nodejs-client/src/client/validation.ts#L62-L98","documentation":"Thrown by ensureRating() in validation.ts:80 as a ValidationError. The message feedback API accepts only 'like' or 'dislike' (or null/undefined to clear). Any other string or type is rejected. Called from messageFeedback() in base.ts for both the positional and object-form overloads.","triggerScenarios":"Calling client.messageFeedback(msgId, 'Like', user) (wrong case), client.messageFeedback(msgId, 'up', user), or client.messageFeedback({ messageId, user, rating: 1 }). null and undefined pass through as 'clear feedback'.","commonSituations":"UI controls that emit 'up'/'down' or +1/-1; case differences from user input; numeric ratings from a 5-star widget routed into a binary field.","solutions":["Map your UI value to the SDK vocabulary: const rating = vote === 'up' ? 'like' : 'dislike'.","Pass null/undefined (not 'none') when clearing feedback.","Constrain the rating type to 'like' | 'dislike' | null at the call site."],"exampleFix":"// before\nawait client.messageFeedback(msgId, 'Like', user)\n\n// after\nawait client.messageFeedback(msgId, 'like', user)","handlingStrategy":"type-guard","validationCode":"function normalizeRating(value: unknown): 'like' | 'dislike' | undefined {\n  if (value === undefined || value === null) return undefined\n  if (value === 'like' || value === 'dislike') return value\n  if (value === 'up' || value === 1 || value === true) return 'like'\n  if (value === 'down' || value === -1 || value === false) return 'dislike'\n  throw new Error(`rating must be 'like' or 'dislike'`)\n}","typeGuard":"function isRating(value: unknown): value is 'like' | 'dislike' {\n  return value === 'like' || value === 'dislike'\n}","tryCatchPattern":"try {\n  await client.messageFeedback(msgId, rating, user)\n} catch (err) {\n  if (err instanceof Error && /rating must be either/.test(err.message)) {\n    // map UI vote to canonical string and retry\n    await client.messageFeedback(msgId, rating === 'up' ? 'like' : 'dislike', user)\n  } else throw err\n}","preventionTips":["Map UI vote values to 'like'/'dislike' in one place.","Type rating as 'like' | 'dislike' | null in your DTOs.","Pass undefined/null (not 'none') to clear feedback."],"tags":["validation","enum","feedback","rating","message"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}