{"record":{"id":"17aace93148917a1","repo":"angular/angular-cli","slug":"project-projectname-contains-extension-with-i","errorCode":null,"errorMessage":"Project '${projectName}' contains extension with invalid name (${name}).","messagePattern":"Project '(.+?)' contains extension with invalid name \\((.+?)\\)\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/angular_devkit/core/src/workspace/json/reader.ts","lineNumber":217,"sourceCode":"        }\n        hasTargets = true;\n        targets = parseTargetsObject(projectName, nodes, context);\n        jsonMetadata.hasLegacyTargetsName = name === 'architect';\n        break;\n      }\n      case 'prefix':\n      case 'root':\n      case 'sourceRoot':\n        if (typeof value !== 'string') {\n          context.warn(`Project property \"${name}\" should be a string.`, value);\n        }\n        if (properties) {\n          properties[name] = value as string;\n        }\n        break;\n      default:\n        if (!context.unprefixedProjectExtensions.has(name) && !/^[a-z]{1,3}-.*/.test(name)) {\n          context.warn(\n            `Project '${projectName}' contains extension with invalid name (${name}).`,\n            name,\n          );\n        }\n        if (extensions) {\n          extensions[name] = value;\n        }\n        break;\n    }\n  }\n\n  let collectionListener: DefinitionCollectionListener<TargetDefinition> | undefined;\n  if (context.trackChanges) {\n    collectionListener = (name, newValue, collection) => {\n      if (hasTargets) {\n        jsonMetadata.addChange(['projects', projectName, 'targets', name], newValue, 'target');\n      } else {\n        jsonMetadata.addChange(","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/core/src/workspace/json/reader.ts#L199-L235","documentation":"While parsing a project in angular.json, any unrecognized key (a project extension) must either be a known unprefixed extension or match the 'aaa-*' (1-3 lowercase letters + dash) naming convention; otherwise the reader warns that the project contains an extension with an invalid name. The value is still retained as an extension.","triggerScenarios":"parseProject's default branch sees a key not in root/prefix/sourceRoot/projectType/architect/targets/generator sets, fails /^[a-z]{1,3}-.*/, and is not in unprefixedProjectExtensions (e.g. \"myPlugin\": {...} inside a project).","commonSituations":"Third-party tools injecting project-level config under arbitrary keys; hand-added custom settings; typos of known extensions like 'schematics' or 'cli' at project level.","solutions":["Rename the key to the namespaced form 'xxx-name' (1-3 lowercase letters then dash), e.g. 'nx-myPlugin'.","Correct the spelling if it was meant to be a known extension key.","Move the setting to the tool's own config file if it does not belong in angular.json."],"exampleFix":"// before (angular.json project)\n\"my-app\": { \"root\": \"\", \"myPlugin\": {} }\n// after\n\"my-app\": { \"root\": \"\", \"x-myPlugin\": {} }","handlingStrategy":"validation","validationCode":"const KNOWN = new Set(['root', 'prefix', 'sourceRoot', 'projectType', 'architect', 'targets', 'generators', 'schematics', 'cli', 'implicitDependencies', 'tags']);\nconst json = require('./angular.json');\nfor (const [name, p] of Object.entries(json.projects ?? {})) {\n  for (const key of Object.keys(p)) {\n    if (!KNOWN.has(key) && !/^[a-z]{1,3}-.*/.test(key)) {\n      console.warn(`projects.${name}: invalid extension name '${key}' (use 'xxx-name' form)`);\n    }\n  }\n}","typeGuard":"function isValidProjectExtensionName(name) {\n  return /^[a-z]{1,3}-.*/.test(name);\n}","tryCatchPattern":null,"preventionTips":["Namespace custom project keys as 'vendor-name' (1-3 lowercase letters + dash).","Check third-party tool docs for their required angular.json key names before adding them.","Avoid typos of standard keys (schematics/generators/targets) at project level."],"tags":["angular","workspace-configuration","angular-json","naming"],"backgroundTag":"invalid-extension-name","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}