grafana/k6 · error

the "open" function is only available in the init stage (i.e

Error message

the "open" function is only available in the init stage (i.e. the global scope), see https://grafana.com/docs/k6/latest/using-k6/test-lifecycle/ for more information

What it means

Like require(), open() is restricted to the init stage: the closure installed by setInitGlobals checks vu.state and throws when open() runs where a VU state exists (default function, setup, teardown, handleSummary). Files must be read during initialization because k6 caches their contents and distributes them to every VU before the test starts.

Source

Thrown at internal/js/bundle.go:478

func (b *Bundle) setInitGlobals(rt *sobek.Runtime, vu *moduleVUImpl, modSys *modules.ModuleSystem) {
	mustSet := func(k string, v any) {
		if err := rt.Set(k, v); err != nil {
			panic(fmt.Errorf("failed to set '%s' global object: %w", k, err))
		}
	}

	impl := requireImpl{
		inInitContext: func() bool { return vu.state == nil },
		modSys:        modSys,
	}

	mustSet("require", impl.require)

	mustSet("open", func(filename string, args ...string) (sobek.Value, error) {
		// TODO fix in stack traces
		if vu.state != nil {
			return nil, fmt.Errorf(cantBeUsedOutsideInitContextMsg, "open")
		}

		if filename == "" {
			return nil, errors.New("open() can't be used with an empty filename")
		}
		// This uses the pwd from the requireImpl
		pwd, err := modSys.CurrentlyRequiredModule()
		if err != nil {
			return nil, err
		}
		if !strings.HasPrefix(filename, "file://") && !filepath.IsAbs(filename) {
			otherPath, shouldWarn := modSys.ShouldWarnOnParentDirNotMatchingCurrentModuleParentDir(vu, pwd)
			logger := b.preInitState.Logger
			if shouldWarn {
				logger.Warningf("open() was used and is currently relative to '%s', but in the future "+
					"it will be aligned with how `require` and imports work and will be relative to '%s'. This means "+
					"that in the future open will open relative path relative to the module/file it is written in. "+
					"You can future proof this by using `import.meta.resolve()` to get relative paths to the file it "+

View on GitHub (pinned to 93accf6570)

Solutions

  1. Open the file at the top level and capture the result in a const, then use that const inside the default function
  2. For large shared datasets, load the content into a SharedArray at init
  3. Remember setup()/teardown() are not the init stage either - open() is unavailable there

Example fix

// before: open() inside the default function
export default function () {
  const data = JSON.parse(open('./users.json'));
}

// after: read at init, share via SharedArray
import { SharedArray } from 'k6/data';
const users = new SharedArray('users', () => JSON.parse(open('./users.json')).users);
export default function () {
  const user = users[__ITER % users.length];
}
Defensive patterns

Strategy: validation

Validate before calling

# heuristic static check: open() on an indented line runs in the VU stage
grep -nP '^[[:space:]]+.*\bopen\(' script.js && \
  { echo 'open() found outside top-level init code'; exit 1; }

Prevention

When it happens

Trigger: Calling open('data.json') inside export default function, setup(), teardown(), or helpers invoked from them; passing open as a callback; attempting per-iteration file reads.

Common situations: Data-driven tests trying to read fixtures per iteration; porting Node.js scripts that call fs.readFile anywhere; moving file reads inside functions during refactoring.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/7fa6690109cdd473. Report an issue: GitHub.