{"record":{"id":"b283f366e4eb5495","repo":"lyswhut/lx-music-desktop","slug":"rule-key-missing","errorCode":null,"errorMessage":"${rule.key} missing","messagePattern":"(.+?) missing","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/core/useApp/useDeeplink/utils.js","lineNumber":36,"sourceCode":"  if (!sources.includes(source)) throw new Error('Source no match')\n}\n\nexport const qualitys = ['128k', '320k', 'flac', 'flac24bit']\nexport const qualityFilter = (source, types) => {\n  types = types.filter(({ type }) => qualitys.includes(type)).map(({ type, size, hash }) => {\n    if (size != null && typeof size != 'string') throw new Error(type + ' size type no match')\n    if (source == 'kg' && typeof hash != 'string') throw new Error(type + ' hash type no match')\n    return hash == null ? { type, size } : { type, size, hash }\n  })\n  if (!types.length) throw new Error('quality no match')\n  return types\n}\n\nexport const dataVerify = (rules, data) => {\n  const newData = {}\n  for (const rule of rules) {\n    const val = data[rule.key]\n    if (rule.required && val == null) throw new Error(rule.key + ' missing')\n    if (val != null) {\n      if (rule.types && !rule.types.includes(typeof val)) throw new Error(rule.key + ' type no match')\n      if (rule.max && String(val).length > rule.max) throw new Error(rule.key + ' max length no match')\n      if (rule.min && String(val).length > rule.min) throw new Error(rule.key + ' min length no match')\n    }\n    newData[rule.key] = val\n  }\n  return newData\n}\n","sourceCodeStart":18,"sourceCodeEnd":46,"githubUrl":"https://github.com/lyswhut/lx-music-desktop/blob/9c364b482e5621a1d38b50e8610d2fb974457e6e/src/renderer/core/useApp/useDeeplink/utils.js#L18-L46","documentation":"dataVerify (utils.js:36) is the generic deep-link payload validator. For any rule with `required: true`, a null/undefined value throws '<key> missing'. The interpolated key name pinpoints exactly which required field was absent. The rule sets live in useMusicAction.js (per-source) and useSonglistAction.js.","triggerScenarios":"Any deep link missing a required field — e.g. a 'play' link missing `songmid`, `name`, `source`, `types`, or the provider-specific required key (hash for kw/kg, strMediaMid for tx, copyrightId for mg). The throw fires before any field is copied into newData.","commonSituations":"The sender omitted a field; a field was renamed between protocol versions; the wrong dataVerify rule set was applied for the chosen source.","solutions":["Read the key from the message and ensure the sender provides it under that exact name.","Cross-check against the per-source rule list in useMusicAction.js (e.g. 'tx' requires strMediaMid; 'mg' requires copyrightId).","If the field is legitimately optional, remove `required: true` from its rule.","Catch upstream and surface the missing-key name to the user via useDialog()."],"exampleFix":"// before\n    if (rule.required && val == null) throw new Error(rule.key + ' missing')\n\n// after - include which rule and that it is required\n    if (rule.required && val == null) {\n      throw new Error(rule.key + ' missing (required field was null/undefined)')\n    }","handlingStrategy":"validation","validationCode":"// generic pre-check against the same rule set used by dataVerify\nconst findMissingRequired = (rules, data) =>\n  rules.filter(r => r.required && data[r.key] == null).map(r => r.key)\nconst missing = findMissingRequired(rules, data)\nif (missing.length) {\n  showErrorDialog(`Missing required field(s): ${missing.join(', ')}`)\n  return\n}","typeGuard":"const hasRequiredFields = (rules, data) =>\n  rules.filter(r => r.required).every(r => data[r.key] != null)","tryCatchPattern":"try {\n  info = dataVerify(rules, info)\n} catch (e) {\n  errorDialog(e.message) // message names the missing key\n}","preventionTips":["Keep the per-source rule lists in useMusicAction.js in sync with what senders actually emit.","Read the interpolated key name to pinpoint the exact missing field.","Validate required fields at the router so the error is user-facing, not an unhandled throw."],"tags":["deeplink","validation","required-field"],"backgroundTag":null,"analyzedSha":"9c364b482e5621a1d38b50e8610d2fb974457e6e","analyzedAt":"2026-08-12T15:14:48.244Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}