go-delve/delve · error

cycle in load graph

Error message

cycle in load graph

What it means

The Starlark module loader in Delve's scripting REPL caches modules and tracks in-progress loads. If a module is requested while its own load is still in progress (the cache contains a nil placeholder for it), the loader returns 'cycle in load graph' to break infinite recursion — Starlark load statements must form a DAG.

Source

Thrown at pkg/terminal/starbind/repl.go:187

}

// MakeLoad returns a simple sequential implementation of module loading
// suitable for use in the REPL.
// Each function returned by MakeLoad accesses a distinct private cache.
func MakeLoad() func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
	type entry struct {
		globals starlark.StringDict
		err     error
	}

	var cache = make(map[string]*entry)

	return func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
		e, ok := cache[module]
		if e == nil {
			if ok {
				// request for package whose loading is in progress
				return nil, errors.New("cycle in load graph")
			}

			// Add a placeholder to indicate "load in progress".
			cache[module] = nil

			// Load it.
			thread := &starlark.Thread{Name: "exec " + module, Load: thread.Load}
			globals, err := execFileOptions(nil, thread, module, nil, nil)
			e = &entry{globals, err}

			// Update the cache.
			cache[module] = e
		}
		return e.globals, e.err
	}
}

View on GitHub (pinned to a23773e6c3)

Solutions

  1. Break the cycle: move the shared code into a third module both scripts load instead of loading each other.
  2. Remove self-loads: a module should never `load()` itself.
  3. Restructure so imports form a tree: leaf utility modules load nothing; higher-level scripts load the leaves.

Example fix

// before: a.star
load("b.star", "helper")
// b.star
load("a.star", "setup")  # cycle!
// after: shared.star
helper = ...  # move shared code here
// a.star
load("shared.star", "helper")
// b.star
load("shared.star", "helper")
Defensive patterns

Strategy: validation

Validate before calling

def check_no_cycle(load_graph, entry):
    seen, stack = set(), []
    def visit(m):
        if m in stack: raise Exception("cycle at " + m)
        if m in seen: return
        seen.add(m); stack.append(m)
        for d in load_graph.get(m, []): visit(d)
        stack.pop()
    visit(entry)

Prevention

When it happens

Trigger: Two Starlark scripts loading each other (a.star loads b.star, b.star loads a.star), or a script loading itself (`load("a.star", ...)` inside a.star).

Common situations: Refactoring scripts into shared modules and accidentally creating mutual imports; copy-pasting load statements so a module re-imports its importer.

Related errors


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/17876a7f8fada7f9. Report an issue: GitHub.