gastownhall/beads · error

ExternalDoltConfig: TLSKey set without TLSCert

Error message

ExternalDoltConfig: TLSKey set without TLSCert

What it means

An edge is rejected when its source and target resolve to the same endpoint — i.e. the new issue would depend on itself (or, when Issue.ID matches a referenced ID, the edge collapses onto the new issue). This wraps domain.ErrSelfDependency with the endpoint ID and storage.ErrValidation. Self-dependencies are logically meaningless and would corrupt dependency graphs/cycle detection.

Solutions

  1. Remove the dependency entry whose TargetID equals the new issue's ID.
  2. Skip such edges when building dependency lists programmatically.
  3. Check before submitting: reject any dep where TargetID == request.Issue.ID (or ParentID == Issue.ID).

Example fix

// before
Issue.ID = "bd-1"; req.Dependencies = []publicops.DependencyInput{{TargetID: "bd-1", Type: types.DepBlocks}}
// after
req.Dependencies = slices.DeleteFunc(req.Dependencies, func(d publicops.DependencyInput) bool { return d.TargetID == issue.ID })
Defensive patterns

Strategy: validation

Validate before calling

if req.Issue != nil && req.Issue.ID != "" {
    if req.ParentID == req.Issue.ID { return errors.New("self parent") }
    for _, d := range req.Dependencies {
        if d.TargetID == req.Issue.ID { return errors.New("self dependency: " + d.TargetID) }
    }
}

Try / catch

if err := store.ExecuteCreate(ctx, req); err != nil {
    if errors.Is(err, storage.ErrValidation) && errors.Is(err, domain.ErrSelfDependency) { /* drop the self edge */ }
    return err
}

Prevention

When it happens

Trigger: ExecuteCreate/ValidatePublicCreateRequest where a dependency target equals the new issue's ID (endpointFor maps it to the new-issue endpoint), or request.ParentID equals Issue.ID; check at public_create.go:198.

Common situations: Auto-generating dependency lists that accidentally include the issue being created; reverse dependencies where the new issue's own ID was used as TargetID; copy-paste of an ID template with the same value in both fields.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/90b1520e206f1129. Report an issue: GitHub.

Appendix: source

Thrown at internal/configfile/external_dolt_config.go:67

	case hasHost && !hasPort:
		return errors.New("ExternalDoltConfig: Host requires Port")
	case !hasHost && hasPort:
		return errors.New("ExternalDoltConfig: Port requires Host")
	}

	if hasHost && (c.Port < 1 || c.Port > 65535) {
		return fmt.Errorf("ExternalDoltConfig: Port %d out of range [1, 65535]", c.Port)
	}

	if hasSocket && !filepath.IsAbs(c.Socket) {
		return fmt.Errorf("ExternalDoltConfig: Socket %q is not absolute", c.Socket)
	}

	switch {
	case c.TLSCert != "" && c.TLSKey == "":
		return errors.New("ExternalDoltConfig: TLSCert set without TLSKey")
	case c.TLSCert == "" && c.TLSKey != "":
		return errors.New("ExternalDoltConfig: TLSKey set without TLSCert")
	}

	if c.TLSCert != "" && !filepath.IsAbs(c.TLSCert) {
		return fmt.Errorf("ExternalDoltConfig: TLSCert %q is not absolute", c.TLSCert)
	}
	if c.TLSKey != "" && !filepath.IsAbs(c.TLSKey) {
		return fmt.Errorf("ExternalDoltConfig: TLSKey %q is not absolute", c.TLSKey)
	}
	if c.TLSCACert != "" && !filepath.IsAbs(c.TLSCACert) {
		return fmt.Errorf("ExternalDoltConfig: TLSCACert %q is not absolute", c.TLSCACert)
	}

	if !c.TLSRequired {
		switch {
		case c.TLSCACert != "":
			return errors.New("ExternalDoltConfig: TLSCACert set without TLSRequired")
		case c.TLSCert != "" || c.TLSKey != "":
			return errors.New("ExternalDoltConfig: TLSCert/TLSKey set without TLSRequired")

View on GitHub (pinned to 71377f2769)