{"record":{"id":"cf41464330cb32b9","repo":"affaan-m/ECC","slug":"refusing-to-manage-claude-hooks-for-a-non-claude-target","errorCode":null,"errorMessage":"Refusing to manage Claude hooks for a non-Claude target.","messagePattern":"Refusing to manage Claude hooks for a non-Claude target\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install-lifecycle.js","lineNumber":560,"sourceCode":" * carry, because Claude Code reports unknown keys when the plugin loads. A\n * sidecar that does not line up with hooks.json is rejected before repair can\n * reconcile matchers under the wrong ids.\n *\n * @param {string} hooksPath - Path to the source hooks.json.\n * @returns {object} the hooks configuration with ids and descriptions restored.\n */\nfunction readHooksConfigNoFollow(hooksPath) {\n  const hooksConfig = readJsonNoFollow(hooksPath);\n  const metadataPath = metadataPathFor(hooksPath);\n  if (!fs.existsSync(metadataPath)) {\n    return hooksConfig;\n  }\n  return mergeHooksMetadata(hooksConfig, readJsonNoFollow(metadataPath), hooksPath);\n}\n\nfunction assertClaudeSettingsDestination(operation, trustedRoot, target = null) {\n  if (target && target !== 'claude' && target !== 'claude-project') {\n    throw new Error('Refusing to manage Claude hooks for a non-Claude target.');\n  }\n  assertClaudeSettingsPath(operation.destinationPath, trustedRoot);\n}\n\nfunction writeContainedFile(destinationPath, content, trustedRoot, action, mode) {\n  const preparedDestination = prepareContainedWriteDestination(destinationPath, trustedRoot, action);\n  const finalDestination = getManagedDestination(\n    preparedDestination,\n    trustedRoot,\n    action\n  ).managedPath;\n  writeFileNoFollow(\n    finalDestination,\n    content,\n    mode,\n    trustedRoot,\n    action\n  );","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install-lifecycle.js#L542-L578","documentation":"assertClaudeSettingsDestination guards hook-management operations so they only ever touch Claude settings files (settings.json) for Claude targets ('claude' or 'claude-project'). If the operation's target is a different harness (e.g. codex, cursor) the library refuses, and it additionally verifies the destination path is the canonical Claude settings path inside the trusted root. This prevents merging Claude hook metadata into another product's config.","triggerScenarios":"Calling materializeManagedHooks/repairManagedHooks/validateManagedHooks/uninstallManagedHooks/updateSettingsAtomic with target set to a non-Claude value (any string other than 'claude' or 'claude-project'), or with an operation whose destinationPath is not the Claude settings path.","commonSituations":"Wiring the Claude hooks workflow into a multi-harness installer and passing target='codex' by mistake; install-state records copied from a Claude operation into a Cursor/Codex operation; typos in target like 'claude-code' which is not accepted.","solutions":["Pass target='claude' (user-level) or 'claude-project' (project-level) when invoking Claude hook-management functions.","Ensure the operation's destinationPath points at the canonical Claude settings.json inside the trusted root.","If you meant to manage hooks for another harness, use that harness's own adapter/operation kind instead of the Claude settings path.","Fix typo'd target values in install-state or calling code (e.g. 'claude-code' -> 'claude')."],"exampleFix":"// before\nawait updateSettingsAtomic(operation, trustedRoot, { target: 'claude-code' })\n\n// after\nawait updateSettingsAtomic(operation, trustedRoot, { target: 'claude' })","handlingStrategy":"validation","validationCode":"function assertClaudeTarget(target) {\n  if (target !== 'claude' && target !== 'claude-project') {\n    throw new Error(`Claude hook management requires target 'claude' or 'claude-project', got: ${target}`);\n  }\n}","typeGuard":"function isClaudeTarget(t) {\n  return t === 'claude' || t === 'claude-project';\n}","tryCatchPattern":"try {\n  await updateSettingsAtomic(op, trustedRoot, { target });\n} catch (err) {\n  if (/non-Claude target/.test(err.message)) {\n    console.error(`Use a Claude target or the adapter for '${target}' instead.`);\n  } else throw err;\n}","preventionTips":["Centralize target constants ('claude', 'claude-project') in one module to avoid typo drift.","Only call Claude hook-management functions from Claude-specific adapters.","Validate the target field in install-state before dispatching to settings functions."],"tags":["hooks","validation","target"],"backgroundTag":"invalid-argument-value","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}