{"record":{"id":"04b9a868b0dc78fb","repo":"TheAlgorithms/JavaScript","slug":"both-keyword-and-message-must-be-specified","errorCode":null,"errorMessage":"Both keyword and message must be specified","messagePattern":"Both keyword and message must be specified","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"Ciphers/KeywordShiftedAlphabet.js","lineNumber":83,"sourceCode":"  return encryptedAlphabet\n}\n\nfunction translate(sourceAlphabet, targetAlphabet, message) {\n  return message.split('').reduce((encryptedMessage, char) => {\n    const isUpperCase = char === char.toUpperCase()\n    const encryptedCharIndex = sourceAlphabet.indexOf(char.toLowerCase())\n    const encryptedChar =\n      encryptedCharIndex !== -1 ? targetAlphabet[encryptedCharIndex] : char\n    encryptedMessage += isUpperCase\n      ? encryptedChar.toUpperCase()\n      : encryptedChar\n    return encryptedMessage\n  }, '')\n}\n\nfunction checkInputs(keyword, message) {\n  if (!keyword || !message) {\n    throw new Error('Both keyword and message must be specified')\n  }\n\n  if (!checkKeywordValidity(keyword)) {\n    throw new Error('Invalid keyword!')\n  }\n}\n\nfunction encrypt(keyword, message) {\n  checkInputs(keyword, message)\n  return translate(\n    alphabet,\n    getEncryptedAlphabet(keyword.toLowerCase()),\n    message\n  )\n}\n\nfunction decrypt(keyword, message) {\n  checkInputs(keyword, message)","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/TheAlgorithms/JavaScript/blob/5c39e87a9a31f279c60f830ad74a845e4788a517/Ciphers/KeywordShiftedAlphabet.js#L65-L101","documentation":"Thrown by checkInputs() when either keyword or message is falsy. The cipher needs both to operate: the keyword builds the shifted alphabet and the message is the text to translate. Any falsy value (empty string, null, undefined, 0) for either is rejected.","triggerScenarios":"Passing '' for keyword or message, null, undefined, 0, NaN, or omitting an argument (defaults to undefined).","commonSituations":"A form field left blank, an optional config key that defaulted to undefined, or destructuring that dropped one of the two required values.","solutions":["Provide non-empty keyword and message strings.","Default both to '' only after confirming presence, or require them at the API boundary.","Validate keyword.trim() and message.trim() before calling encrypt/decrypt."],"exampleFix":"// before\nencrypt(opts.keyword, opts.message) // one is undefined\n// after\nif (opts.keyword?.trim() && opts.message?.trim()) {\n  encrypt(opts.keyword, opts.message)\n}","handlingStrategy":"validation","validationCode":"function encryptIfPresent(keyword, message) {\n  if (!keyword?.toString().trim() || !message?.toString().trim()) {\n    throw new Error('Both keyword and message must be specified');\n  }\n  return encrypt(keyword, message);\n}","typeGuard":"/** @param {unknown} k @param {unknown} m @returns {boolean} */\nconst hasBoth = (k, m) =>\n  typeof k === 'string' && typeof m === 'string' && k.trim() !== '' && m.trim() !== '';","tryCatchPattern":"try { return encrypt(keyword, message); }\ncatch (e) {\n  if (e instanceof Error && /must be specified/.test(e.message)) {\n    // require both fields in the UI before retrying\n  } else throw e;\n}","preventionTips":["Require both fields in the form/UI before calling encrypt/decrypt.","Trim and check non-empty at the boundary.","Avoid passing destructured values that may be undefined."],"tags":["cipher","keyword-shifted","input-validation","required-argument"],"backgroundTag":null,"analyzedSha":"5c39e87a9a31f279c60f830ad74a845e4788a517","analyzedAt":"2026-08-13T04:54:54.474Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}