gofr-dev/gofr · error

failed to create migration directory %q: %w

Error message

failed to create migration directory %q: %w

What it means

Thrown by checkAndCreateMigrationTable when os.MkdirAll cannot create the directory that should contain the OpenTSDB migration file. The wrapped error identifies the OS-level cause (permissions, path is a file, read-only FS). Migration setup aborts before any migration runs.

Source

Thrown at pkg/gofr/migration/opentsdb.go:56

// apply initializes openTSDBMigrator using the openTsdbDS.
func (ds openTSDBDS) apply(m migrator) migrator {
	return &openTSDBMigrator{ // Return pointer to avoid copying the mutex
		filePath: ds.filePath,
		migrator: m,
	}
}

// checkAndCreateMigrationTable ensures the migration directory and file structure exists.
// It only creates an empty file if no migration file exists at all.
func (om *openTSDBMigrator) checkAndCreateMigrationTable(c *container.Container) error {
	om.mu.Lock()
	defer om.mu.Unlock()

	// Ensure directory exists
	dir := filepath.Dir(om.filePath)
	if dir != "." {
		if err := os.MkdirAll(dir, dirPerm); err != nil {
			return fmt.Errorf("failed to create migration directory %q: %w", dir, err)
		}
	}

	// Check if file exists and is readable
	if _, err := os.Stat(om.filePath); err == nil {
		// File exists, validate it's proper JSON
		return om.validateExistingFile(c)
	} else if !os.IsNotExist(err) {
		// Some other error accessing the file
		return fmt.Errorf("unexpected error stating migration file: %w", err)
	}

	// File doesn't exist, create empty migration file
	return om.createEmptyMigrationFile(c)
}

// validateExistingFile checks if the existing migration file is valid JSON.
func (om *openTSDBMigrator) validateExistingFile(c *container.Container) error {

View on GitHub (pinned to 187eb24962)

Solutions

  1. Make the parent directory writable or choose a writable filePath (e.g. /var/lib/app)
  2. Check the wrapped OS error — EACCES vs ENOTDIR vs EROFS
  3. Remove/replace any regular file that conflicts with the directory path
  4. Pre-create the directory with correct ownership before deploying

Example fix

// before
filePath: "/etc/gofr_migrations.json" // MkdirAll(/etc) fails: permission denied
// after
filePath: "/var/lib/myapp/gofr_migrations.json"
Defensive patterns

Strategy: validation

Validate before calling

dir := filepath.Dir(cfg.OpenTSDBMigrationFile)
if info, err := os.Stat(dir); err == nil && !info.IsDir() {
    return fmt.Errorf("%s exists but is a file; remove it so a directory can be created", dir)
}
if err := os.MkdirAll(dir, 0755); err != nil {
    return fmt.Errorf("pre-flight mkdir failed: %w", err)
}

Prevention

When it happens

Trigger: filepath.Dir(om.filePath) is not "." and os.MkdirAll(dir, 0755) fails: parent not writable, a path component exists as a regular file, or the filesystem is read-only.

Common situations: filePath configured as /etc/gofr_migrations.json (no write perms); a file occupies what should be a directory; running in a locked-down container rootfs; NFS mount gone read-only.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/03d2996dfe603ce1. Report an issue: GitHub.