{"record":{"id":"243cbd9513bc5b78","repo":"vercel/turborepo","slug":"invalid-task-name-found-in-turbo-json","errorCode":null,"errorMessage":"Invalid task name found in turbo.json.","messagePattern":"Invalid task name found in turbo\\.json\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/eslint-plugin-turbo/lib/utils/calculate-inputs.ts","lineNumber":232,"sourceCode":"\nfunction environmentTestArray(envContext: EnvironmentTest) {\n  return [\n    envContext.legacyConfig,\n    envContext.env,\n    envContext.passThroughEnv,\n    envContext.dotEnv\n  ];\n}\n\n// Identify where to store `EnvironmentConfig`s\n\nfunction getTaskAddress(taskName: string): {\n  workspaceName: string | null;\n  scriptName: string;\n} {\n  // Somehow empty. Error.\n  if (taskName.length === 0) {\n    throw new Error(\"Invalid task name found in turbo.json.\");\n  }\n\n  const firstIndexOf = taskName.indexOf(\"#\");\n\n  // Something like \"build\"\n  if (firstIndexOf === -1) {\n    return {\n      workspaceName: null,\n      scriptName: taskName\n    };\n  }\n\n  // Something like \"what#are#you#doing\"\n  if (firstIndexOf !== taskName.lastIndexOf(\"#\")) {\n    throw new Error(\"Invalid task name found in turbo.json.\");\n  }\n\n  const [workspaceName, scriptName] = taskName.split(\"#\");","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/vercel/turborepo/blob/9f94a7d215b3881942527dd526afa3fc650869d5/packages/eslint-plugin-turbo/lib/utils/calculate-inputs.ts#L214-L250","documentation":"eslint-plugin-turbo computes task inputs (for rules like no-undeclared-env-vars) by parsing every task key in turbo.json. getTaskAddress splits each key into workspace/script parts; a task key that is the empty string has no script name, so the plugin throws this Error while the ESLint rule builds its input map.","triggerScenarios":"A turbo.json whose `tasks` (or legacy `pipeline`) object contains an empty-string key, e.g. `\"tasks\": { \"\": { \"outputs\": [\"dist\"] } }`. Typically produced by templating bugs, JSON generation/merging, or hand edits.","commonSituations":"Generated turbo.json from scripts that map over an empty/blank task list; config merge artifacts; YAML-to-JSON conversions emitting an empty key; the error surfaces as an ESLint crash when the plugin rule runs.","solutions":["Open turbo.json and delete the empty `\"\"` key under tasks/pipeline","Validate the config with `npx turbo run build --dry=json` — the turbo CLI will reject the same invalid shape","Point your editor at the turbo.json JSON schema (https://turborepo.com/schema.json) to catch invalid keys as you type"],"exampleFix":"// turbo.json (before)\n\"tasks\": { \"\": { \"outputs\": [\"dist\"] }, \"build\": { } }\n// after\n\"tasks\": { \"build\": { \"outputs\": [\"dist\"] } }","handlingStrategy":"validation","validationCode":"function hasEmptyTaskKey(turboJson: { tasks?: Record<string, unknown>; pipeline?: Record<string, unknown> }): boolean {\n  const tasks = turboJson.tasks ?? turboJson.pipeline ?? {};\n  return Object.keys(tasks).some((k) => k.length === 0);\n}\n// gate CI: if (hasEmptyTaskKey(turboJson)) fail fast with a clear message","typeGuard":"function isValidTaskKey(key: string): boolean {\n  return key.length > 0 && key.indexOf('#') === key.lastIndexOf('#');\n}","tryCatchPattern":null,"preventionTips":["Never build tasks objects from dynamic keys without filtering out empty strings","Add `npx turbo <task> --dry=json` to CI — the CLI validates the same shape the ESLint plugin parses","Enable the turbo.json $schema in editors for immediate key validation"],"tags":["eslint-plugin-turbo","turbo-json","config","validation","linting"],"backgroundTag":"invalid-config-key","analyzedSha":"9f94a7d215b3881942527dd526afa3fc650869d5","analyzedAt":"2026-08-16T19:47:29.531Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}