{"record":{"id":"8cec6033887016ac","repo":"aureuserp/aureuserp","slug":"invalid-rule-s-configuration-the-following-rule-c","errorCode":null,"errorMessage":"Invalid rule's configuration, the following rule causes an endless loop: :name","messagePattern":"Invalid rule's configuration, the following rule causes an endless loop: :name","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"plugins/webkul/inventories/src/Models/Product.php","lineNumber":182,"sourceCode":"    }\n\n    public function resolveRuleChain($location, $routes = null, ?Collection $seen = null): Collection\n    {\n        $seen ??= collect();\n\n        $warehouse = $location->warehouse ?? ($seen->isNotEmpty() ? $seen->last()?->propagateWarehouse : null);\n\n        $rule = InventoryFacade::findRule($this, $location, [\n            'routes'    => $routes ?: collect(),\n            'warehouse' => $warehouse,\n        ]);\n\n        if (! $rule) {\n            return $seen;\n        }\n\n        if ($seen->contains(fn ($seenRule) => $seenRule->id === $rule->id)) {\n            throw new \\Exception(__('inventories::system.product.endless-loop-rule', ['name' => $rule->name]));\n        }\n\n        $seen->push($rule);\n\n        if (\n            $rule->procure_method === ProcureMethod::MAKE_TO_STOCK\n            || ! in_array($rule->action, [RuleAction::PULL_PUSH, RuleAction::PULL], true)\n        ) {\n            return $seen;\n        }\n\n        return $this->resolveRuleChain($rule->sourceLocation, null, $seen);\n    }\n\n    public function procurementDates($date, $location, $routes = null): array\n    {\n        $rules = $this->resolveRuleChain($location, $routes);\n","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/aureuserp/aureuserp/blob/bd7cbeeb0c620985dfd9b88c56be7f3ea86be6b4/plugins/webkul/inventories/src/Models/Product.php#L164-L200","documentation":"Product::resolveRuleChain() walks pull/push rules starting from a location. Each hop finds the next rule via InventoryFacade::findRule(); if the chain reaches a rule that was already visited (same id), the configuration is a cycle and it throws with the offending rule's name instead of looping forever.","triggerScenarios":"Two or more PULL_PUSH/PULL rules whose source/destination locations feed each other (rule A pulls X->Y, rule B pulls Y->X), reachable from the product's location or warehouse; the throw happens when resolving procure method chains (MTS-else-MTO) during confirmation or procurement runs.","commonSituations":"Copy-pasted rules across global and warehouse routes with overlapping locations; a rule's source location set to another rule's destination; route changes or upgrades re-linking rules into a loop.","solutions":["Open the named rule and break the cycle: change its source location so the chain no longer returns to a visited location","Set the looping rule's procure method to MTS (MAKE_TO_STOCK) — the walk stops at MTS rules","Delete or disable one of the mutually-referencing rules"],"exampleFix":"// before (cycle)\n// Rule A: WH/Stock -> WH/Shelf 1, action pull_push, procure mts_else_mto\n// Rule B: WH/Shelf 1 -> WH/Stock, action pull_push\n$product->resolveRuleChain($location); // throws endless loop\n\n// after\n// Rule B: source changed to Vendors, procure mts -> chain terminates\n$product->resolveRuleChain($location);","handlingStrategy":"validation","validationCode":"// walk rules yourself with a visited set before running procurement\n$seen = collect();\n$loc = $startLocation;\nwhile (true) {\n    $rule = InventoryFacade::findRule($product, $loc, ['warehouse' => $loc->warehouse]);\n    if (! $rule || $rule->procure_method === ProcureMethod::MAKE_TO_STOCK) {\n        break;\n    }\n    if ($seen->contains(fn ($r) => $r->id === $rule->id)) {\n        throw new \\RuntimeException(\"Rule cycle detected at {$rule->name}\");\n    }\n    $seen->push($rule);\n    $loc = $rule->sourceLocation;\n}","typeGuard":null,"tryCatchPattern":"try {\n    $chain = $product->resolveRuleChain($location);\n} catch (\\Exception $e) {\n    // message names the looping rule: fix route configuration, do not retry unchanged\n    report($e);\n}","preventionTips":["When creating pull/push rules, verify no rule's source location equals another rule's destination on the same chain","Prefer MTS procure method on terminal rules so chains terminate","Add a rule-cycle lint (visited-set walk) to configuration import tooling"],"tags":["inventory","routes","rules","procurement","infinite-loop"],"backgroundTag":"cyclic-rule-configuration","analyzedSha":"bd7cbeeb0c620985dfd9b88c56be7f3ea86be6b4","analyzedAt":"2026-08-21T06:41:00.338Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}