{"record":{"id":"94e145dd83a9aa47","repo":"ReactiveX/rxjs","slug":"outputroot-is-required-when-write-is-enabled","errorCode":null,"errorMessage":"outputRoot is required when write is enabled.","messagePattern":"outputRoot is required when write is enabled\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/migrate/src/node.ts","lineNumber":168,"sourceCode":"    canonicalOutputs.add(canonicalOutputPath);\n    await assertWritableOutput(resolvedOutputPath, options.overwrite ?? false);\n  }\n}\n\nasync function assertWritableOutput(outputPath: string, overwrite: boolean): Promise<void> {\n  try {\n    const outputStats = await lstat(outputPath);\n    if (outputStats.isSymbolicLink()) throw new Error(`Refusing to overwrite a symbolic link: ${outputPath}`);\n    if (!outputStats.isFile()) throw new Error(`Output path is not a regular file: ${outputPath}`);\n    if (!overwrite) throw new Error(`Output path already exists; enable overwrite explicitly: ${outputPath}`);\n  } catch (error: unknown) {\n    if (!isMissingPathError(error)) throw error;\n  }\n}\n\nexport async function migrateTestFiles(options: MigrateFilesOptions): Promise<readonly MigratedFile[]> {\n  if (options.write && !options.outputRoot) {\n    throw new Error('outputRoot is required when write is enabled.');\n  }\n  const plan = await planMigrationFiles(options);\n  return options.write ? applyMigrationPlan(plan, { overwrite: options.overwrite }) : plan.files;\n}\n\nfunction safeOutputPath(outputRoot: string, outputName: string): string {\n  if (!outputName || outputName === '.' || isAbsolute(outputName)) {\n    throw new Error(`Output name must be a non-empty relative path: ${outputName || '<empty>'}`);\n  }\n  const outputPath = resolve(outputRoot, outputName);\n  if (outputPath === outputRoot) throw new Error(`Output name must identify a file below outputRoot: ${outputName}`);\n  assertContained(outputRoot, outputPath, `Output path is outside outputRoot: ${outputName}`);\n  return outputPath;\n}\n\nfunction assertContained(root: string, candidate: string, message: string): void {\n  const localPath = relative(root, candidate);\n  if (localPath === '' || (localPath !== '..' && !localPath.startsWith(`..${sep}`) && !isAbsolute(localPath))) return;","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/migrate/src/node.ts#L150-L186","documentation":"migrateTestFiles is the one-call convenience wrapper: it plans and (when options.write is true) applies the plan. Writing requires a concrete destination, so the very first thing the function checks is that outputRoot is provided whenever write is enabled; without it the migrator would have to write files alongside their sources with no way to control the destination.","triggerScenarios":"Calling migrateTestFiles({ write: true }) without an outputRoot property (or with it set to undefined/empty).","commonSituations":"Adapting a dry-run script (which legitimately omits outputRoot) to actually write by adding write:true; typoing the option name; assuming outputRoot defaults to sourceRoot when writing.","solutions":["Add outputRoot pointing at the destination directory for the migrated files","If you only wanted a dry run, remove write:true instead of adding outputRoot","Derive outputRoot from sourceRoot explicitly (e.g. path.join(sourceRoot, 'migrated')) if you want a predictable default"],"exampleFix":"// before\nawait migrateTestFiles({ files, sourceRoot, write: true });\n\n// after\nawait migrateTestFiles({ files, sourceRoot, outputRoot: path.join(sourceRoot, 'migrated'), write: true });","handlingStrategy":"type-guard","validationCode":"if (options.write && !options.outputRoot) {\n  throw new Error('outputRoot is required when write is enabled.'); // fail fast with your own message\n}","typeGuard":"interface WriteOptions { write?: boolean; outputRoot?: string }\nfunction isWritableConfig(o: WriteOptions): o is WriteOptions & { write: true; outputRoot: string } {\n  return o.write === true && typeof o.outputRoot === 'string' && o.outputRoot.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Centralize migrateTestFiles option construction in one helper that always sets outputRoot when write is true","Treat missing outputRoot as a dry-run-only configuration"],"tags":["migrate","configuration","missing-option"],"backgroundTag":"missing-required-option","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}