temporalio/temporal · error
link type is empty
Error message
link type is empty
What it means
validateLinkType in common/nexus/nexusrpc/api.go checks that a Nexus link's type string is non-empty and composed only of alphanumeric characters, '_', '.', or '/'. When the type is an empty string the function rejects it with this error, since a link without a type is meaningless for routing/identification. It is returned by encodeLink and decodeLink when converting Nexus links to/from header representations.
Source
Thrown at common/nexus/nexusrpc/api.go:244
}
return link, nil
}
func validateLinkURL(value *url.URL) error {
if value == nil || value.String() == "" {
return errors.New("url is empty")
}
_, err := url.ParseQuery(value.RawQuery)
if err != nil {
return fmt.Errorf("url query not percent-encoded: %s", value)
}
return nil
}
func validateLinkType(value string) error {
if len(value) == 0 {
return errors.New("link type is empty")
}
for _, c := range value {
if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '_' && c != '.' && c != '/' {
return errors.New("link type contains invalid char (valid chars: alphanumeric, '_', '.', '/')")
}
}
return nil
}
var durationRegexp = regexp.MustCompile(`^(\d+(?:\.\d+)?)(ms|s|m)$`)
func ParseDuration(value string) (time.Duration, error) {
m := durationRegexp.FindStringSubmatch(value)
if len(m) == 0 {
return 0, fmt.Errorf("invalid duration: %q", value)
}
v, err := strconv.ParseFloat(m[1], 64)
if err != nil {View on GitHub (pinned to bde624efd1)
Solutions
- Set a non-empty Type on the Link before encoding/decoding (e.g. the service or component name the link points to)
- Verify the code producing the link metadata actually populates the type field
- If parsing from headers, check the raw header value contains the type segment before calling decodeLink
Example fix
// before
link := &nexus.Link{URL: u, Type: ""}
// after
link := &nexus.Link{URL: u, Type: "temporal.api.failure.v1"} Defensive patterns
Strategy: validation
Validate before calling
func validLinkType(t string) bool {
if t == "" { return false }
for _, c := range t {
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '.' || c == '/') { return false }
}
return true
}
// call validLinkType(link.Type) before encodeLink/decodeLink Type guard
if link == nil || link.Type == "" { return fmt.Errorf("link type missing") } Try / catch
if err := nexusrpc.ValidateLinkType(link.Type); err != nil {
return fmt.Errorf("invalid nexus link type %q: %w", link.Type, err)
} Prevention
- Always initialize Link.Type from a known-good constant or service name
- Mirror the allowed charset (alphanumerics, '_', '.', '/') in any UI/config that collects link types
- Add unit tests covering empty and special-character link types
When it happens
Trigger: Calling encodeLink or decodeLink with a Link whose Type field is the empty string (e.g. a Link struct built manually without setting Type, or decoding a header where the type portion was omitted/blank).
Common situations: Manually constructing nexus links for callbacks in workflow code; an upstream producer writing link metadata headers without the type; trimming/parsing bugs that strip the type segment from a header value.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- link type contains invalid char (valid chars: alphanumeric,
- empty operation name
- empty operation token
- ErrInvalidOperationToken
- second value out of range: %v
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/291e94364e60c2b3.
Report an issue: GitHub.