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.
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)
Solutions
- Remove the dependency entry whose TargetID equals the new issue's ID.
- Skip such edges when building dependency lists programmatically.
- 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
- Filter dependencies whose TargetID equals the new issue's ID.
- Watch for Reverse edges that point the new issue at itself.
- Guard generated dependency lists against self-references.
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
- no store is open for this workspace
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
- ExternalDoltConfig: must set Socket or (Host, Port)
- ExternalDoltConfig: Host requires Port
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/90b1520e206f1129.
Report an issue: GitHub.