{"record":{"id":"e0c8c26286d9d0b0","repo":"jackwener/OpenCLI","slug":"tap-missing-params","errorCode":"TAP_MISSING_PARAMS","errorMessage":"tap: store and action are required","messagePattern":"tap: store and action are required","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"src/pipeline/steps/tap.ts","lineNumber":42,"sourceCode":"  args?: unknown[];\n}\n\nexport async function stepTap(\n  page: IPage | null,\n  params: unknown,\n  data: unknown,\n  args: Record<string, unknown>,\n): Promise<unknown> {\n  const cfg: TapParams = typeof params === 'object' && params !== null ? (params as TapParams) : {};\n  const storeName = String(render(cfg.store ?? '', { args, data }));\n  const actionName = String(render(cfg.action ?? '', { args, data }));\n  const capturePattern = String(render(cfg.capture ?? '', { args, data }));\n  const timeout = cfg.timeout ?? 5;\n  const selectPath = cfg.select ? String(render(cfg.select, { args, data })) : null;\n  const framework = cfg.framework ?? null;\n  const actionArgs: unknown[] = cfg.args ?? [];\n\n  if (!storeName || !actionName) throw new CliError('TAP_MISSING_PARAMS', 'tap: store and action are required');\n\n  // Build select chain for the captured response\n  const selectChain = selectPath\n    ? selectPath.split('.').map((p: string) => `?.[${JSON.stringify(p)}]`).join('')\n    : '';\n\n  // Serialize action arguments\n  const actionArgsRendered = actionArgs.map((a) => {\n    const rendered = render(a, { args, data });\n    return JSON.stringify(rendered);\n  });\n  const actionCall = actionArgsRendered.length\n    ? `store[${JSON.stringify(actionName)}](${actionArgsRendered.join(', ')})`\n    : `store[${JSON.stringify(actionName)}]()`;\n\n  // Use shared interceptor generator for fetch/XHR patching\n  const tap = generateTapInterceptorJs(JSON.stringify(capturePattern));\n","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/pipeline/steps/tap.ts#L24-L60","documentation":"stepTap requires both a store name and an action name in its config, because a tap interacts with a framework store by dispatching a named action. If either is missing after config resolution, it throws CliError with code TAP_MISSING_PARAMS. This is a fail-fast configuration validation so the tap does not silently do nothing.","triggerScenarios":"Calling a tap step whose cfg is missing store (storeName falsy) or action (actionName falsy), or where the rendered values are empty strings. Any invocation of stepTap with partial config triggers this before any selector/timeout work begins.","commonSituations":"Copy-pasting a tap step and forgetting to fill in the action field, a template like render(cfg.store, ...) evaluating to an empty string because its args were absent, renaming a store/action in app code without updating the pipeline config.","solutions":["Add both store and action fields to the tap step config","Verify the rendered values are non-empty (check that template args/data actually resolve)","Confirm the store and action names match what the app registers"],"exampleFix":"// before\n{ type: 'tap', store: 'cart', select: '.total', capture: '{{total}}' }\n// after\n{ type: 'tap', store: 'cart', action: 'addItem', select: '.total', capture: '{{total}}' }","handlingStrategy":"validation","validationCode":"if (!cfg.store || !cfg.action) {\n  throw new Error('tap step requires non-empty \"store\" and \"action\"');\n}","typeGuard":"function hasTapParams(cfg: unknown): cfg is { store: string; action: string } & Record<string, unknown> {\n  return typeof cfg === 'object' && cfg !== null\n    && typeof (cfg as any).store === 'string' && (cfg as any).store.length > 0\n    && typeof (cfg as any).action === 'string' && (cfg as any).action.length > 0;\n}","tryCatchPattern":"try {\n  await stepTap(cfg, args, data);\n} catch (e) {\n  if ((e as any).code === 'TAP_MISSING_PARAMS') {\n    console.error('tap config invalid — set both store and action:', cfg);\n  } else throw e;\n}","preventionTips":["Include both store and action in every tap step template/snippet","Check that template args actually render to non-empty strings for store/action","Validate pipeline step configs (required fields per step type) before execution","Update tap configs whenever store or action names change in the app"],"tags":["configuration","validation","missing-param"],"backgroundTag":"missing-required-parameter","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}