d2lang/d2 · error

parent "_" can only be used in the beginning of paths, e.g.

Error message

parent "_" can only be used in the beginning of paths, e.g. "_.x"

What it means

In d2 path resolution, the `_` token means 'parent'. It is only meaningful as a leading token chain at the start of a path (e.g. `_.x`, `_._.x`). A `_` appearing after a normal identifier is ambiguous, so resolution fails with this error.

Source

Thrown at d2graph/d2graph.go:804

					referencesActor = true
					break
				}
			}
			if referencesActor {
				obj = objSD
			}
		}
	}

	resolvedObj = obj
	resolvedIDA = ida

	for i, id := range ida {
		if id != "_" {
			continue
		}
		if i != 0 && ida[i-1] != "_" {
			return nil, nil, errors.New(`parent "_" can only be used in the beginning of paths, e.g. "_.x"`)
		}
		if resolvedObj == obj.Graph.Root {
			return nil, nil, errors.New(`parent "_" cannot be used in the root scope`)
		}
		if i == len(ida)-1 {
			return nil, nil, errors.New(`invalid use of parent "_"`)
		}
		resolvedObj = resolvedObj.Parent
		resolvedIDA = resolvedIDA[1:]
	}

	return resolvedObj, resolvedIDA, nil
}

// TODO: remove edges []edge and scope each edge inside Object.
func (obj *Object) FindEdges(mk *d2ast.Key) ([]*Edge, bool) {
	if len(mk.Edges) != 1 {
		return nil, false

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Move all `_` tokens to the beginning of the path: use `_.a.b` instead of `a._.b`.
  2. If you meant the object literally named with an underscore path, restructure so the reference is relative from the current scope, e.g. write `_.a` at the right nesting level.
  3. If the target is absolute, use a full path from root instead of mixing `_` mid-path.
  4. Quote the identifier if you actually meant a literal `_` character as part of a name.

Example fix

// before
a._.b
// after
_.a.b
Defensive patterns

Strategy: validation

Validate before calling

func validParentPath(path string) bool {
  segs := strings.Split(path, ".")
  for i, s := range segs {
    if s == "_" && i != 0 && segs[i-1] != "_" { return false }
  }
  return true
}

Type guard

func hasValidUnderscoreUse(ida []string) bool {
  for i, id := range ida {
    if id == "_" && i != 0 && ida[i-1] != "_" { return false }
  }
  return true
}

Try / catch

obj, ida, err := g.MiddlePath(obj, ida)
if err != nil && strings.Contains(err.Error(), `parent "_"`) {
  // rewrite the path with _ tokens at the front and retry
}

Prevention

When it happens

Trigger: Resolving/compiling a path such as `a._.b` or `x.y._` where `_` appears at index > 0 and the previous token is not `_` — e.g. referencing `a._.b` inside a map key or via d2oracle paths.

Common situations: Writing relative references like `parent.child._.other` assuming `_` works anywhere in the path; tools generating paths programmatically appending `_` mid-path; misunderstanding that `_` is only a prefix operator.

Related errors


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/4503c9635dc31ba7. Report an issue: GitHub.