Skip to content

bug: magic sets rewriting rejects intermediate variables when first rule arg is bound by literal keyword #297

Closed

Description

@adityamukho

Description

In v1.2.0, the magic sets rewriting introduced in #289 incorrectly rejects queries where a rule's first argument is bound by a literal keyword at the call site and the rule body contains an intermediate variable (one that appears only in the body, not in the rule head).

Reproduction

(transact [[:a :edge :b]])
(transact [[:b :edge :c]])

(rule [(reach ?x ?y) [?x :edge ?y]])
(rule [(reach ?x ?y) [?x :edge ?mid] (reach ?mid ?y)])

; This works (all variables free):
(query [:find ?y :where (reach ?a ?y)])

; This FAILS with "Unbound variable in rule head: ?mid":
(query [:find ?y :where (reach :a ?y)])

The error Unbound variable in rule head: ?mid is incorrect — ?mid is a valid intermediate (existential) variable in the rule body; it does not need to appear in the head.

Affected versions

Introduced in v1.2.0 (magic sets rewriting, #289). v1.1.1 is not affected.

Workaround

Look up the target entity via a fact pattern and pass the bound variable into the rule:

[?a :some/attr "value"]
(reach ?a ?y)

Impact

Breaks all recursive rule queries where the starting entity is given as a literal keyword, including the reports-to, transfer-chain, and reachable examples in minigraf-playground (steps o3-s2, o3-s3, sl3-s3, t3-s3).

Activity

  1. added this to the v1.2.1 milestone on Jun 28, 2026
  2. added a commit that references this issue on Jun 28, 2026
  3. added a commit that references this issue on Jun 28, 2026
  4. adityamukho commented on Sep 26, 2026

    @adityamukho
    CollaboratorAuthor

    Reopening. This still fails in @minigraf/browser 2.0.1.

    The fix in #300 covers the repro above, where the recursive rule body uses a plain fact pattern. That case now works. It still fails when the recursive rule body calls another user-defined rule instead of a fact pattern.

    Repro (2.0.1)

    (transact [[:a :next :b] [:b :next :c]])
    
    (rule [(link ?x ?y) [?x :next ?y]])
    (rule [(chain ?x ?y) (link ?x ?y)])
    (rule [(chain ?x ?y) (link ?x ?mid) (chain ?mid ?y)])
    
    ; Fails: "datalog evaluator: Unbound variable in rule head: ?mid"
    (query [:find ?y :where (chain :a ?y)])
    
    ; Works, returns [[":b"] [":c"]]:
    (query [:find ?y :where [?a :next :b] (chain ?a ?y)])
    

    I get the same error on 1.2.0 and on 2.0.1.

    Impact

    minigraf-playground steps sl3-s3 (transfer-chain over transfer-link) and t3-s3 (reachable over same-line) still need the pattern-bind workaround. The org-chart steps (o3-s2, o3-s3) recurse over a plain fact pattern, and those now work with a literal keyword. See project-minigraf/minigraf-playground#40.


    Generated by Claude Code

  5. modified the milestones: v1.2.1, v2.0.2 on Sep 26, 2026
  6. added 2 commits that reference this issue on Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions