{"record":{"id":"9a3e5d0c2b0e4295","repo":"zenorocha/clipboard.js","slug":"invalid-action-value-use-either-copy-or-cut","errorCode":null,"errorMessage":"Invalid \"action\" value, use either \"copy\" or \"cut\"","messagePattern":"Invalid \"action\" value, use either \"copy\" or \"cut\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/actions/default.js","lineNumber":15,"sourceCode":"import ClipboardActionCut from './cut';\nimport ClipboardActionCopy from './copy';\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\nconst ClipboardActionDefault = (options = {}) => {\n  // Defines base properties passed from constructor.\n  const { action = 'copy', container, target, text } = options;\n\n  // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n  if (action !== 'copy' && action !== 'cut') {\n    throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n  }\n\n  // Sets the `target` property using an element that will be have its content copied.\n  if (target !== undefined) {\n    if (target && typeof target === 'object' && target.nodeType === 1) {\n      if (action === 'copy' && target.hasAttribute('disabled')) {\n        throw new Error(\n          'Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute'\n        );\n      }\n\n      if (\n        action === 'cut' &&\n        (target.hasAttribute('readonly') || target.hasAttribute('disabled'))\n      ) {\n        throw new Error(\n          'Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes'\n        );","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/zenorocha/clipboard.js/blob/899378dee9681dcf4cb3d702c23a3f3cd9f473d8/src/actions/default.js#L1-L33","documentation":"Thrown by ClipboardActionDefault when the `action` option is set to any value other than 'copy' or 'cut'. The option defaults to 'copy' when omitted, so the error only fires when an explicit invalid string is passed. The library enforces this because only copy and cut have corresponding selection/execution strategies wired up.","triggerScenarios":"Passing `new ClipboardJS(el, { action: () => 'paste' })` whose action function returns a value other than 'copy'/'cut'; calling `ClipboardActionDefault({ action: 'paste' })` directly; passing a typo like 'Copy' (capital C) or 'cunt'/'cit'; passing a non-string such as `action: undefined` won't trigger it (default kicks in) but `action: null` will since `null !== 'copy'`.","commonSituations":"Dynamic `action` functions whose return value comes from a data attribute, dropdown, or external config that yields an unexpected string; case mismatch ('Copy' vs 'copy'); i18n pipelines that translate the literal action word; refactors that change the action source without updating the consumer; passing a bitwise/numeric action code.","solutions":["Verify the value passed to `action` is exactly the lowercase string 'copy' or 'cut' (case-sensitive).","If `action` is computed by a function (e.g., `action: () => someState`), log/inspect that function's return value at runtime and coerce it with `.toLowerCase()`.","If the intent is to let the default apply, omit the `action` option entirely instead of passing `undefined`/`null`.","Where the action originates from user/config input, whitelist it before forwarding: `const a = ['copy','cut'].includes(input) ? input : 'copy';`."],"exampleFix":"// before\nnew ClipboardJS(btn, { action: () => targetEl.getAttribute('data-act') });\n// after\nnew ClipboardJS(btn, {\n  action: () => {\n    const act = targetEl.getAttribute('data-act');\n    return act === 'cut' ? 'cut' : 'copy'; // coerce to a known value\n  }\n});","handlingStrategy":"validation","validationCode":"const VALID_ACTIONS = new Set(['copy', 'cut']);\nfunction resolveAction(raw) {\n  if (raw === undefined || raw === null) return 'copy'; // let default apply\n  const a = String(raw).toLowerCase();\n  if (!VALID_ACTIONS.has(a)) {\n    throw new Error(`Unsupported clipboard action: ${JSON.stringify(raw)}`);\n  }\n  return a;\n}\n// usage\nnew ClipboardJS(btn, { action: () => resolveAction(getUserAction()) });","typeGuard":"// narrow a dynamic value to the allowed action union\n/**\n * @param {unknown} v\n * @returns {v is 'copy'|'cut'}\n */\nfunction isClipboardAction(v) {\n  return v === 'copy' || v === 'cut';\n}","tryCatchPattern":"try {\n  ClipboardActionDefault({ action: maybeAction, target: el });\n} catch (err) {\n  if (/Invalid \"action\" value/.test(err.message)) {\n    console.warn('Clipboard action ignored; falling back to copy', maybeAction);\n    ClipboardActionDefault({ action: 'copy', target: el });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Treat action as an enum, never a free-form string — derive it from a single constant map.","When action comes from a data attribute or URL param, whitelist it before forwarding.","Add a unit test asserting the action function can only ever return 'copy' or 'cut'.","Avoid uppercase variants in the data layer; normalize at the boundary."],"tags":["clipboard","validation","input-validation","api-misuse"],"backgroundTag":null,"analyzedSha":"899378dee9681dcf4cb3d702c23a3f3cd9f473d8","analyzedAt":"2026-08-13T04:33:09.334Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}