dgraph-io/dgraph · error

expand() not allowed inside shortest

Error message

expand() not allowed inside shortest

What it means

Dgraph rejects a query that uses expand(...), the recursive edge-expansion function, inside a shortest-path block. expand() is only meaningful for regular subgraph traversal; combined with shortest() it would make the path search undefined or unbounded, so treeCopy fails fast during query parsing. The check runs for any direct child of the query whose Params.Alias is "shortest".

Source

Thrown at query/query.go:538

		// empty because MathExp should have atleast one of them.
		key = fmt.Sprintf("val(%+v)", gchild.Var)
	}
	if gchild.IsGroupby {
		key += "groupby"
	}
	return key
}

func treeCopy(gq *dql.GraphQuery, sg *SubGraph) error {
	// Typically you act on the current node, and leave recursion to deal with
	// children. But, in this case, we don't want to muck with the current
	// node, because of the way we're dealing with the root node.
	// So, we work on the children, and then recurse for grand children.
	attrsSeen := make(map[string]struct{})

	for _, gchild := range gq.Children {
		if sg.Params.Alias == "shortest" && gchild.Expand != "" {
			return errors.Errorf("expand() not allowed inside shortest")
		}

		key := ""
		if gchild.Alias != "" {
			key = gchild.Alias
		} else {
			key = uniqueKey(gchild)
		}
		if _, ok := attrsSeen[key]; ok {
			return errors.Errorf("%s not allowed multiple times in same sub-query.",
				key)
		}
		attrsSeen[key] = struct{}{}

		args := params{
			Alias:        gchild.Alias,
			Expand:       gchild.Expand,
			Facet:        gchild.Facets,

View on GitHub (pinned to 759e242be6)

Solutions

  1. Remove the expand() child from inside the shortest path block
  2. List the specific predicates you want the path to traverse as children instead of expand
  3. If you need generic traversal plus shortest path, run two separate queries and combine results client-side

Example fix

// before
query {
  shortest(from: 0x1, to: 0x2) {
    expand(_all_)
  }
}
// after
query {
  shortest(from: 0x1, to: 0x2) {
    friend
    knows
  }
}
Defensive patterns

Strategy: validation

Validate before calling

const shortestBlocks = [...parsedQuery.matchAll(/shortest\s*\(([^)]*)\)\s*\{([^}]*)\}/g)];
for (const [, , body] of shortestBlocks) {
  if (/expand\s*\(/.test(body)) throw new Error('expand() is not allowed inside shortest; list predicates explicitly');
}

Type guard

function isShortestWithExpand(child) { return child.alias === 'shortest' && typeof child.expand === 'string' && child.expand.length > 0; }

Try / catch

try {
  const res = await dgraph.newTxn().query(q);
} catch (e) {
  if (e.message.includes('expand() not allowed inside shortest')) {
    throw new Error('Query rewrite needed: remove expand() from shortest block');
  }
  throw e;
}

Prevention

When it happens

Trigger: Sending a GraphQL+- query with a shortest path block containing an expand(_) child, e.g. `shortest(from: uid, to: uid) { expand(_all_) }`. Produced in treeCopy when Params.Alias == "shortest" and gchild.Expand is non-empty.

Common situations: Developers migrating traversal queries to shortest-path queries copy an expand() line into the shortest block; tutorials mixing path-finding with generic schema exploration; dynamic query builders that append expand to every level.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/b6026bc2dc9aa26d. Report an issue: GitHub.