{"record":{"id":"cfa838c8d812fa22","repo":"gastownhall/beads","slug":"cannot-extract-work-item-id-from-url-s","errorCode":null,"errorMessage":"cannot extract work item ID from URL: %s","messagePattern":"cannot extract work item ID from URL: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ado/links.go","lineNumber":33,"sourceCode":"// and ADO work item relations.\ntype LinkResolver struct {\n\tClient *Client\n}\n\n// NewLinkResolver creates a new LinkResolver with the given client.\nfunc NewLinkResolver(client *Client) *LinkResolver {\n\treturn &LinkResolver{Client: client}\n}\n\n// workItemIDPattern extracts a work item ID from an ADO API URL.\n// Handles URLs with query parameters (e.g. ?api-version=7.1).\nvar workItemIDPattern = regexp.MustCompile(`/(\\d+)(?:\\?|$)`)\n\n// extractWorkItemID extracts the numeric ID from an ADO work item API URL.\nfunc extractWorkItemID(url string) (int, error) {\n\tmatches := workItemIDPattern.FindStringSubmatch(url)\n\tif len(matches) < 2 {\n\t\treturn 0, fmt.Errorf(\"cannot extract work item ID from URL: %s\", url)\n\t}\n\tid, err := strconv.Atoi(matches[1])\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid work item ID in URL %s: %w\", url, err)\n\t}\n\treturn id, nil\n}\n\n// isLinkRelation checks if a relation type is a work item link (vs attachment, etc).\nfunc isLinkRelation(rel string) bool {\n\treturn strings.HasPrefix(rel, \"System.LinkTypes.\")\n}\n\n// discoveredFromComment is the marker attribute used to identify discovered-from links\n// stored as ADO Related relations.\nconst discoveredFromComment = \"beads:discovered-from\"\n\n// adoRelToBeadsDep maps an ADO relation type to a beads dependency type.","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/ado/links.go#L15-L51","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: parsing every relation URL blindly\nid, err := extractWorkItemID(rel.URL)\n// after: skip URLs that aren't API work item URLs\nif !strings.Contains(rel.URL, \"/_apis/wit/workItems/\") {\n    continue // not a work item link\n}\nid, err := extractWorkItemID(rel.URL)","handlingStrategy":"validation","validationCode":"var workItemURLRe = regexp.MustCompile(`/\\d+(?:\\?|$)`)\nfunc isParseableWorkItemURL(u string) bool { return workItemURLRe.MatchString(u) }","typeGuard":"func extractWorkItemIDSafe(url string) (int, bool) {\n    id, err := extractWorkItemID(url)\n    return id, err == nil\n}","tryCatchPattern":"id, err := extractWorkItemID(rel.URL)\nif err != nil {\n    log.Printf(\"skipping unparseable relation URL %q: %v\", rel.URL, err)\n    continue\n}","preventionTips":["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."],"tags":["azure-devops","url-parsing","link-sync"],"backgroundTag":"url-parse-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}