gastownhall/beads · error

invalid work item ID in URL %s: %w

Error message

invalid work item ID in URL %s: %w

What it means

extractWorkItemID matched a numeric-looking segment in the work item URL, but strconv.Atoi failed to convert it to an int. This happens only when the digits overflow int (IDs longer than ~19-20 digits) since the regex guarantees only digits. It wraps the strconv error for diagnosis.

Source

Thrown at internal/ado/links.go:37

// NewLinkResolver creates a new LinkResolver with the given client.
func NewLinkResolver(client *Client) *LinkResolver {
	return &LinkResolver{Client: client}
}

// workItemIDPattern extracts a work item ID from an ADO API URL.
// Handles URLs with query parameters (e.g. ?api-version=7.1).
var workItemIDPattern = regexp.MustCompile(`/(\d+)(?:\?|$)`)

// extractWorkItemID extracts the numeric ID from an ADO work item API URL.
func extractWorkItemID(url string) (int, error) {
	matches := workItemIDPattern.FindStringSubmatch(url)
	if len(matches) < 2 {
		return 0, fmt.Errorf("cannot extract work item ID from URL: %s", url)
	}
	id, err := strconv.Atoi(matches[1])
	if err != nil {
		return 0, fmt.Errorf("invalid work item ID in URL %s: %w", url, err)
	}
	return id, nil
}

// isLinkRelation checks if a relation type is a work item link (vs attachment, etc).
func isLinkRelation(rel string) bool {
	return strings.HasPrefix(rel, "System.LinkTypes.")
}

// discoveredFromComment is the marker attribute used to identify discovered-from links
// stored as ADO Related relations.
const discoveredFromComment = "beads:discovered-from"

// adoRelToBeadsDep maps an ADO relation type to a beads dependency type.
// Returns the dep type and whether the from/to should be swapped
// (true for reverse link types that need direction normalization).
// Hierarchy links use beads storage vocabulary "parent-child" (types.DepParentChild),
// not the legacy "parent" type, which was only ever produced by the pre-fix

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the URL in the error; verify the trailing number is a genuine ADO work item ID.
  2. On 32-bit platforms, build/run on 64-bit (int = 64 bits) so large IDs fit.
  3. Correct or remove the malformed relation in Azure DevOps and resync.
  4. If IDs legitimately exceed int range in your environment, file/patch the library to parse IDs as int64.

Example fix

// before
id, err := strconv.Atoi(matches[1])
// after
id64, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
    return 0, fmt.Errorf("invalid work item ID in URL %s: %w", url, err)
}
id = int(id64)
Defensive patterns

Strategy: validation

Validate before calling

func workItemIDInRange(url string) bool {
    m := workItemIDPattern.FindStringSubmatch(url)
    if len(m) < 2 { return false }
    n, err := strconv.ParseInt(m[1], 10, 64)
    return err == nil && n > 0 && n <= math.MaxInt32
}

Type guard

func parseWorkItemID(url string) (int, bool) {
    id, err := extractWorkItemID(url)
    if err != nil { return 0, false }
    return id, true
}

Try / catch

id, err := extractWorkItemID(url)
if err != nil {
    var numErr *strconv.NumError
    if errors.As(err, &numErr) {
        log.Printf("work item ID overflow in %s", url)
    }
    return err
}

Prevention

When it happens

Trigger: A relation URL whose trailing numeric segment exceeds platform int range (e.g. a 20+ digit number), typically from malformed or synthetic data.

Common situations: Imported/test data with absurdly long numeric segments; corrupted relation URLs; using the library on a 32-bit platform where int is 32 bits and IDs exceed 2147483647 (very large ADO orgs/IDs).

Related errors


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