{"record":{"id":"44ea9c35e39ed8ac","repo":"angular/angular-cli","slug":"expected-name-to-be-a-string","errorCode":null,"errorMessage":"Expected name to be a string.","messagePattern":"Expected name to be a string\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/architect/src/jobs/simple-registry.ts","lineNumber":88,"sourceCode":"    options: RegisterJobOptions = {},\n  ): void {\n    // Switch on the arguments.\n    if (typeof nameOrHandler == 'string') {\n      if (!isJobHandler(handlerOrOptions)) {\n        // This is an error.\n        throw new TypeError('Expected a JobHandler as second argument.');\n      }\n\n      this._register(nameOrHandler, handlerOrOptions, options);\n    } else if (isJobHandler(nameOrHandler)) {\n      if (typeof handlerOrOptions !== 'object') {\n        // This is an error.\n        throw new TypeError('Expected an object options as second argument.');\n      }\n\n      const name = options.name || nameOrHandler.jobDescription.name || handlerOrOptions.name;\n      if (name === undefined) {\n        throw new TypeError('Expected name to be a string.');\n      }\n\n      this._register(name, nameOrHandler, options);\n    } else {\n      throw new TypeError('Unrecognized arguments.');\n    }\n  }\n\n  protected _register<\n    ArgumentT extends JsonValue,\n    InputT extends JsonValue,\n    OutputT extends JsonValue,\n  >(\n    name: JobName,\n    handler: JobHandler<ArgumentT, InputT, OutputT>,\n    options: RegisterJobOptions,\n  ): void {\n    if (this._jobNames.has(name)) {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/architect/src/jobs/simple-registry.ts#L70-L106","documentation":"register() resolves the job name from (in order) options.name, nameOrHandler.jobDescription.name, then handlerOrOptions.name. If all three are undefined the name cannot be determined, and a TypeError is thrown (the message says string even though the check is === undefined).","triggerScenarios":"register(handler, { description: 'x' }) where neither the options object nor the handler's jobDescription carries a name; creating a JobHandler without passing { name: ... } to createJobHandler and not supplying options.name.","commonSituations":"Constructing handlers manually without jobDescription.name; omitting the name in the options bag while relying on a handler created with an anonymous factory; copies/refactors that stripped the name property.","solutions":["Add a name to the options: registry.register(handler, { name: 'my-job' }).","Give the handler a name at creation: createJobHandler(fn, { name: 'my-job', description: '...' }) so jobDescription.name is populated.","Verify the handler's jobDescription.name is not undefined (e.g. jobDescription was spread without name)."],"exampleFix":"// before\nconst handler = createJobHandler(() => of({ success: true }));\nregistry.register(handler, { description: 'does work' });\n// after\nconst handler = createJobHandler(() => of({ success: true }), { name: 'work-job', description: 'does work' });\nregistry.register(handler);","handlingStrategy":"validation","validationCode":"const resolvedName = opts?.name ?? handler?.jobDescription?.name ?? opts?.name;\nif (typeof resolvedName !== 'string' || resolvedName.length === 0) {\n  throw new Error('job name must be provided via options.name or jobDescription.name');\n}","typeGuard":"function hasJobName(handler, opts) {\n  return typeof (opts?.name ?? handler?.jobDescription?.name) === 'string';\n}","tryCatchPattern":"try {\n  registry.register(handler, opts);\n} catch (e) {\n  if (e instanceof TypeError && /Expected name to be a string/.test(e.message)) {\n    throw new Error(`job has no name: set options.name or jobDescription.name on the handler`);\n  }\n  throw e;\n}","preventionTips":["Always pass { name: 'my-job' } to createJobHandler","Never spread jobDescription without verifying the name property survives","Assert handler.jobDescription.name exists in a shared test helper","Give every registered job an explicit name rather than relying on defaults"],"tags":["typescript","typeerror","architect","job-registry","missing-name"],"backgroundTag":"required-property-missing","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}