kopia/kopia · error

writing diagnostics output requires a non-empty directory…

Error message

writing diagnostics output requires a non-empty directory name (specified with the '--diagnostics-output-directory' flag)

What it means

Thrown by the observability flags initialize step when diagnostics output is requested (--save-metrics, --save-profiles, or --profile-cpu) but --diagnostics-output-directory is empty. Kopia needs a directory to write traces/profiles and refuses to guess one.

Solutions

  1. Add --diagnostics-output-directory <dir> alongside the profiling flags.
  2. Remove the --save-metrics/--save-profiles/--profile-cpu flags if diagnostics are not needed.
  3. Ensure the script variable feeding the flag is non-empty, e.g. guard: [ -n "$DIAG_DIR" ] || exit 1.

Example fix

// before
kopia snapshot create --all --save-metrics
// after
kopia snapshot create --all --save-metrics --diagnostics-output-directory /tmp/kopia-diag
Defensive patterns

Strategy: validation

Validate before calling

if wantProfiles && (diagDir == "") { return errors.New("--diagnostics-output-directory required with profiling flags") }

Try / catch

if out, err := cmd.CombinedOutput(); err != nil {
  if strings.Contains(string(out), "non-empty directory name") { /* re-invoke with --diagnostics-output-directory set */ }
}

Prevention

When it happens

Trigger: Running any command with --save-metrics, --save-profiles, or --profile-cpu while omitting --diagnostics-output-directory (or passing an empty string for it).

Common situations: Enabling profiling flags in scripts/config copied from examples that included the directory flag; templated CI invocations where the directory variable is unset and expands to empty.

Understand the failure class

Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/1da05109c74a69b2. Report an issue: GitHub.

Appendix: source

Thrown at cli/observability_flags.go:117

	app.Flag("metrics-store-on-exit", "Writes metrics to a file in a sub-directory of the directory specified with the --diagnostics-output-directory").Hidden().BoolVar(&c.saveMetrics)

	c.pf.setup(app)

	app.PreAction(c.initialize)
}

func (c *observabilityFlags) initialize(ctx *kingpin.ParseContext) error {
	// write to a separate file per command and process execution to avoid
	// conflicts with previously created files
	command := "unknown"
	if cmd := ctx.SelectedCommand; cmd != nil {
		command = strings.ReplaceAll(cmd.FullCommand(), " ", "-")
	}

	c.outputSubdirectoryName = clock.Now().Format("20060102-150405-") + command

	if (c.saveMetrics || c.pf.saveProfiles || c.pf.profileCPU) && c.outputDirectory == "" {
		return errors.New("writing diagnostics output requires a non-empty directory name (specified with the '--diagnostics-output-directory' flag)")
	}

	return nil
}

// spanName specifies the name of the span at the start of a trace. A tracer is
// started only when spanName is not empty.
func (c *observabilityFlags) run(ctx context.Context, spanName string, f func(context.Context) error) error {
	if err := c.start(ctx); err != nil {
		return errors.Wrap(err, "unable to start observability facilities")
	}

	defer c.stop(ctx)

	if err := c.pf.start(ctx, filepath.Join(c.outputDirectory, c.outputSubdirectoryName)); err != nil {
		return errors.Wrap(err, "failed to start profiling")
	}

View on GitHub (pinned to 82495e54b5)