{"record":{"id":"ac3b27c59a2896d4","repo":"docker/compose","slug":"error-parsing-command-line-expected-q","errorCode":null,"errorMessage":"error parsing command line, expected %q","messagePattern":"error parsing command line, expected %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/compose/compose.go","lineNumber":541,"sourceCode":"\t\t\t\tfmt.Fprint(os.Stderr, aec.Apply(\"option '--workdir' is DEPRECATED at root level! Please use '--project-directory' instead.\\n\", aec.RedF))\n\t\t\t}\n\t\t\tfor i, file := range opts.EnvFiles {\n\t\t\t\tfile = composepaths.ExpandUser(file)\n\t\t\t\tif !filepath.IsAbs(file) {\n\t\t\t\t\tfile, err := filepath.Abs(file)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\topts.EnvFiles[i] = file\n\t\t\t\t} else {\n\t\t\t\t\topts.EnvFiles[i] = file\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcomposeCmd := cmd\n\t\t\tfor composeCmd.Name() != PluginName {\n\t\t\t\tif !composeCmd.HasParent() {\n\t\t\t\t\treturn fmt.Errorf(\"error parsing command line, expected %q\", PluginName)\n\t\t\t\t}\n\t\t\t\tcomposeCmd = composeCmd.Parent()\n\t\t\t}\n\n\t\t\tif v, ok := os.LookupEnv(ComposeParallelLimit); ok && !composeCmd.Flags().Changed(\"parallel\") {\n\t\t\t\ti, err := strconv.Atoi(v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"%s must be an integer (found: %q)\", ComposeParallelLimit, v)\n\t\t\t\t}\n\t\t\t\tparallel = i\n\t\t\t}\n\t\t\tif parallel > 0 {\n\t\t\t\tlogrus.Debugf(\"Limiting max concurrency to %d jobs\", parallel)\n\t\t\t\tbackendOptions.Add(compose.WithMaxConcurrency(parallel))\n\t\t\t}\n\n\t\t\t// dry run detection\n\t\t\tif dryRun {","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/cmd/compose/compose.go#L523-L559","documentation":"This is an internal invariant in compose's cobra wiring: the PersistentPreRunE walks up the command tree until it finds the root command named 'compose' (PluginName) to read global flags on it. If the tree has no command with that name (renamed root, standalone miswiring, or a non-standard embedding), the walk hits a parentless command and errors. End users normally never produce it; it signals the binary is being invoked in an unexpected command hierarchy.","triggerScenarios":"Running the compose binary with its root command renamed (custom builds), embedding compose's command tree under a different program name, or a plugin invocation where the expected 'compose' root is absent from the chain.","commonSituations":"Forks/custom builds renaming the root command; invoking the binary via a symlink/wrapper that changes argv[0] semantics combined with odd subcommand routing; version mismatches after partial upgrades of the docker CLI plugin.","solutions":["Invoke compose the supported way: `docker compose ...` so the plugin root is named 'compose'","If you renamed the root in a fork, revert the root command's Use to keep 'compose'","Reinstall/align the compose plugin version with your docker CLI (`docker compose version`)"],"exampleFix":"// before (fork)\nrootCmd := &cobra.Command{Use: \"mytool\"}\n// after\nrootCmd := &cobra.Command{Use: compose.PluginName}","handlingStrategy":"try-catch","validationCode":"# verify the expected command hierarchy before scripting\ndocker compose version >/dev/null 2>&1 || { echo 'compose plugin misinstalled'; exit 1; }","typeGuard":null,"tryCatchPattern":"# fork maintainers: assert the root command name during init\nif rootCmd.Name() != compose.PluginName {\n    return fmt.Errorf(\"root command must be %q, got %q\", compose.PluginName, rootCmd.Name())\n}","preventionTips":["Always invoke via `docker compose` so the plugin root is named correctly","In forks, never rename the root command's Use string"],"tags":["cli","cobra","plugin","internal"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}