{"record":{"id":"17876a7f8fada7f9","repo":"go-delve/delve","slug":"cycle-in-load-graph","errorCode":null,"errorMessage":"cycle in load graph","messagePattern":"cycle in load graph","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/terminal/starbind/repl.go","lineNumber":187,"sourceCode":"}\n\n// MakeLoad returns a simple sequential implementation of module loading\n// suitable for use in the REPL.\n// Each function returned by MakeLoad accesses a distinct private cache.\nfunc MakeLoad() func(thread *starlark.Thread, module string) (starlark.StringDict, error) {\n\ttype entry struct {\n\t\tglobals starlark.StringDict\n\t\terr     error\n\t}\n\n\tvar cache = make(map[string]*entry)\n\n\treturn func(thread *starlark.Thread, module string) (starlark.StringDict, error) {\n\t\te, ok := cache[module]\n\t\tif e == nil {\n\t\t\tif ok {\n\t\t\t\t// request for package whose loading is in progress\n\t\t\t\treturn nil, errors.New(\"cycle in load graph\")\n\t\t\t}\n\n\t\t\t// Add a placeholder to indicate \"load in progress\".\n\t\t\tcache[module] = nil\n\n\t\t\t// Load it.\n\t\t\tthread := &starlark.Thread{Name: \"exec \" + module, Load: thread.Load}\n\t\t\tglobals, err := execFileOptions(nil, thread, module, nil, nil)\n\t\t\te = &entry{globals, err}\n\n\t\t\t// Update the cache.\n\t\t\tcache[module] = e\n\t\t}\n\t\treturn e.globals, e.err\n\t}\n}\n","sourceCodeStart":169,"sourceCodeEnd":204,"githubUrl":"https://github.com/go-delve/delve/blob/a23773e6c31361e43246bc43a424ee009679b174/pkg/terminal/starbind/repl.go#L169-L204","documentation":"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.","triggerScenarios":"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).","commonSituations":"Refactoring scripts into shared modules and accidentally creating mutual imports; copy-pasting load statements so a module re-imports its importer.","solutions":["Break the cycle: move the shared code into a third module both scripts load instead of loading each other.","Remove self-loads: a module should never `load()` itself.","Restructure so imports form a tree: leaf utility modules load nothing; higher-level scripts load the leaves."],"exampleFix":"// before: a.star\nload(\"b.star\", \"helper\")\n// b.star\nload(\"a.star\", \"setup\")  # cycle!\n// after: shared.star\nhelper = ...  # move shared code here\n// a.star\nload(\"shared.star\", \"helper\")\n// b.star\nload(\"shared.star\", \"helper\")","handlingStrategy":"validation","validationCode":"def check_no_cycle(load_graph, entry):\n    seen, stack = set(), []\n    def visit(m):\n        if m in stack: raise Exception(\"cycle at \" + m)\n        if m in seen: return\n        seen.add(m); stack.append(m)\n        for d in load_graph.get(m, []): visit(d)\n        stack.pop()\n    visit(entry)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never write load(\"self.star\") inside a module","Keep the module graph a DAG: shared code in leaf modules with no loads","Audit mutual imports after refactoring scripts into modules"],"tags":["starlark","scripting","imports"],"backgroundTag":"circular-import","analyzedSha":"a23773e6c31361e43246bc43a424ee009679b174","analyzedAt":"2026-08-31T15:12:45.221Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}