gastownhall/beads · error
cannot extract work item ID from URL: %s
Error message
cannot extract work item ID from URL: %s
What it means
extractWorkItemID parses a numeric work item ID out of an Azure DevOps work item URL using the pattern /(\d+)(?:\?|$). If the URL does not end (before an optional query string) in a numeric segment, no ID can be extracted and this error is returned. It protects link-sync code from fabricating dependency edges with bogus IDs.
Source
Thrown at internal/ado/links.go:33
// and ADO work item relations.
type LinkResolver struct {
Client *Client
}
// 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.View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the relation URL in the error and confirm it ends in a numeric work item ID (optionally followed by a query string).
- Fix or remove the malformed relation on the ADO work item, then re-run sync.
- If the URL comes from another tool, normalize it to the ADO API form .../_apis/wit/workItems/<id> before passing it in.
- Pre-filter relations (e.g. with isLinkRelation / URL validation) so non-work-item links are skipped instead of parsed.
Example fix
// before: parsing every relation URL blindly
id, err := extractWorkItemID(rel.URL)
// after: skip URLs that aren't API work item URLs
if !strings.Contains(rel.URL, "/_apis/wit/workItems/") {
continue // not a work item link
}
id, err := extractWorkItemID(rel.URL) Defensive patterns
Strategy: validation
Validate before calling
var workItemURLRe = regexp.MustCompile(`/\d+(?:\?|$)`)
func isParseableWorkItemURL(u string) bool { return workItemURLRe.MatchString(u) } Type guard
func extractWorkItemIDSafe(url string) (int, bool) {
id, err := extractWorkItemID(url)
return id, err == nil
} Try / catch
id, err := extractWorkItemID(rel.URL)
if err != nil {
log.Printf("skipping unparseable relation URL %q: %v", rel.URL, err)
continue
} Prevention
- Only feed ADO REST API URLs (.../_apis/wit/workItems/<id>) into link sync, not browser UI URLs.
- Sanitize imported relations and drop non-work-item targets before syncing.
- Strip trailing slashes from relation URLs before parsing.
When it happens
Trigger: Passing ExtractLinkDeps or PushLinks a relation URL that does not end in /<digits> or /<digits>?query — e.g. a truncated URL, an HTML work-item UI URL like .../_workitems/edit/123 with extra path segments, or an empty/garbage url field.
Common situations: Hand-crafted relations in ADO pointing at non-work-item artifacts; URLs with trailing slashes; copying browser URLs instead of API URLs; importing data from another tracker where relation.target.url points elsewhere.
Related errors
- invalid work item ID in URL %s: %w
- remove relation %d: %w
- add link %s to %d: %w
- cannot use multiple conflict resolution flags
- ado.pat not configured: set via 'bd config set ado.pat <toke
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/cfa838c8d812fa22.
Report an issue: GitHub.