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

  1. Set a non-empty Type on the Link before encoding/decoding (e.g. the service or component name the link points to)
  2. Verify the code producing the link metadata actually populates the type field
  3. 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

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


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/291e94364e60c2b3. Report an issue: GitHub.