thanos-io/thanos · error

close temporary dir

Error message

close temporary dir

What it means

Flush fails when df.Close() cannot close the temporary block directory handle after a successful sync. The close is required before renaming the tmp dir into place (particularly on Windows, where open handles prevent renames).

Solutions

  1. Check open file descriptor limits (ulimit -n) and close leaked handles elsewhere in the app.
  2. Retry the Flush; ensure no double-close of the same dir handle.
  3. On Windows, confirm no other process holds an open handle inside the tmp dir.
  4. Discard the tmp dir and re-create the block if the close keeps failing.

Example fix

// before
$ ulimit -n 1024
// after
$ ulimit -n 65536
Defensive patterns

Strategy: retry

Validate before calling

// ensure fd headroom
var lim unix.Rlimit
if unix.Getrlimit(unix.RLIMIT_NOFILE, &lim) == nil && lim.Cur < 4096 {
    lim.Cur = 4096
    unix.Setrlimit(unix.RLIMIT_NOFILE, &lim)
}

Try / catch

if _, err := d.Flush(ctx); err != nil {
    if strings.Contains(err.Error(), "close temporary dir") {
        // fd/handle issue: GC and retry once, else re-create block
        runtime.GC()
        return fmt.Errorf("closing tmp dir failed: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: df.Close() returning an error — typically fd exhaustion-related state issues, double-close conditions, or OS-level errors on the directory handle.

Common situations: Running out of file descriptors leading to handle corruption; platform quirks on Windows where the dir must be closed before rename; rare kernel-level errors.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/a96902f03c8908ff. Report an issue: GitHub.

Appendix: source

Thrown at pkg/block/writer.go:128

		}
	}()
	df, err := fileutil.OpenDir(d.bTmp)
	if err != nil {
		return tsdb.BlockStats{}, errors.Wrap(err, "open temporary block dir")
	}
	defer func() {
		if df != nil {
			err = tsdb_errors.NewMulti(err, df.Close()).Err()
		}
	}()

	if err := df.Sync(); err != nil {
		return tsdb.BlockStats{}, errors.Wrap(err, "sync temporary dir file")
	}

	// Close temp dir before rename block dir (for windows platform).
	if err = df.Close(); err != nil {
		return tsdb.BlockStats{}, errors.Wrap(err, "close temporary dir")
	}
	df = nil

	if err := tsdb_errors.CloseAll(d.closers); err != nil {
		d.closers = nil
		return tsdb.BlockStats{}, err
	}
	d.closers = nil

	// Block files successfully written, make them visible by moving files from tmp dir.
	if err := fileutil.Replace(filepath.Join(d.bTmp, IndexFilename), filepath.Join(d.bDir, IndexFilename)); err != nil {
		return tsdb.BlockStats{}, errors.Wrap(err, "replace index file")
	}
	if err := fileutil.Replace(filepath.Join(d.bTmp, ChunksDirname), filepath.Join(d.bDir, ChunksDirname)); err != nil {
		return tsdb.BlockStats{}, errors.Wrap(err, "replace chunks dir")
	}
	return d.stats, nil
}

View on GitHub (pinned to 35b8b99117)