{"record":{"id":"cd991b965d980fd8","repo":"hashicorp/nomad","slug":"error-parsing-topics-w","errorCode":null,"errorMessage":"error parsing topics: %w","messagePattern":"error parsing topics: %w","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"command/agent/event_endpoint.go","lineNumber":221,"sourceCode":"\tcodedErr := errs.Wait()\n\tif codedErr != nil && strings.Contains(codedErr.Error(), io.ErrClosedPipe.Error()) {\n\t\tcodedErr = nil\n\t}\n\n\treturn nil, codedErr\n}\n\nfunc parseEventTopics(query url.Values) (map[structs.Topic][]string, error) {\n\traw, ok := query[\"topic\"]\n\tif !ok {\n\t\treturn allTopics(), nil\n\t}\n\ttopics := make(map[structs.Topic][]string)\n\n\tfor _, topic := range raw {\n\t\tk, v, err := parseTopic(topic)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing topics: %w\", err)\n\t\t}\n\n\t\ttopics[structs.Topic(k)] = append(topics[structs.Topic(k)], v)\n\t}\n\treturn topics, nil\n}\n\nfunc parseTopic(topic string) (string, string, error) {\n\tparts := strings.Split(topic, \":\")\n\t// infer wildcard if only given a topic\n\tif len(parts) == 1 {\n\t\treturn topic, \"*\", nil\n\t} else if len(parts) != 2 {\n\t\treturn \"\", \"\", fmt.Errorf(\"Invalid key value pair for topic, topic: %s\", topic)\n\t}\n\treturn parts[0], parts[1], nil\n}\n","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/event_endpoint.go#L203-L239","documentation":"EventStream parses the query string's topic parameters into a map of structs.Topic to key lists via parseEventTopics. Any failure from parseTopic for an individual topic= query parameter is wrapped with this message and returned to the stream request.","triggerScenarios":"GET /v1/event/stream with a topic= query parameter that parseTopic rejects — e.g. a value containing more than one colon such as topic=Deployment:abc:def.","commonSituations":"URL-encoded values containing colons (UUIDs are fine, but timestamps or joined key:value payloads are not); concatenating multiple key=value pairs into a single topic parameter.","solutions":["Use at most one colon per topic parameter: topic=NodeRegistration:node1 or topic=Deployment.","Pass multiple topics as repeated parameters: topic=Evaluation:*&topic=Deployment:*.","URL-encode any colon-bearing payload or split it into separate parameters.","Use the wildcard topic=* to subscribe to everything and filter client-side."],"exampleFix":"// before\nGET /v1/event/stream?topic=Deployment:abc:def\n// after\nGET /v1/event/stream?topic=Deployment:*","handlingStrategy":"validation","validationCode":"function buildTopicParam(key, value = '*') {\n  if (/[\\s]/.test(key) || (value && value.includes(':'))) {\n    throw new Error(`invalid topic ${key}:${value}`)\n  }\n  return `topic=${encodeURIComponent(key)}:${encodeURIComponent(value)}`\n}","typeGuard":"func validTopicParam(s string) bool {\n  return strings.Count(s, \":\") <= 1\n}","tryCatchPattern":"topics, err := parseEventTopics(raw)\nif err != nil {\n  http.Error(w, \"bad topic parameter; use topic=Name:value with at most one colon\", 400)\n  return\n}","preventionTips":["Keep at most one colon per topic parameter.","Use repeated topic= parameters for multiple subscriptions.","URL-encode keys and values with encodeURI/encodeURIComponent.","Prefer wildcard topic=* and filter client-side when unsure."],"tags":["events","parsing","http-api"],"backgroundTag":"invalid-query-parameter","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}