{"record":{"id":"834a03a65c672e70","repo":"bytebase/bytebase","slug":"invalid-prefix-q-in-request-q","errorCode":null,"errorMessage":"invalid prefix %q in request %q","messagePattern":"invalid prefix %q in request %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/common/resource_name.go","lineNumber":624,"sourceCode":"// TrimSuffix trims the suffix from the name and returns the trimmed name.\nfunc TrimSuffix(name, suffix string) (string, error) {\n\tif !strings.HasSuffix(name, suffix) {\n\t\treturn \"\", errors.Errorf(\"invalid request %q with suffix %q\", name, suffix)\n\t}\n\treturn strings.TrimSuffix(name, suffix), nil\n}\n\n// GetNameParentTokens returns the tokens from a resource name.\nfunc GetNameParentTokens(name string, tokenPrefixes ...string) ([]string, error) {\n\tparts := strings.Split(name, \"/\")\n\tif len(parts) != 2*len(tokenPrefixes) {\n\t\treturn nil, errors.Errorf(\"invalid request %q\", name)\n\t}\n\n\tvar tokens []string\n\tfor i, tokenPrefix := range tokenPrefixes {\n\t\tif parts[2*i+1] == \"\" || fmt.Sprintf(\"%s/\", parts[2*i]) != tokenPrefix {\n\t\t\treturn nil, errors.Errorf(\"invalid prefix %q in request %q\", tokenPrefix, name)\n\t\t}\n\t\ttokens = append(tokens, parts[2*i+1])\n\t}\n\treturn tokens, nil\n}\n\nfunc GetWorkspaceID(name string) (string, error) {\n\ttokens, err := GetNameParentTokens(name, WorkspacePrefix)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn tokens[0], nil\n}\n\nfunc FormatWorkspace(id string) string {\n\treturn fmt.Sprintf(\"%s%s\", WorkspacePrefix, id)\n}\n","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/common/resource_name.go#L606-L642","documentation":"GetNameParentTokens parses a resource name (e.g. \"projects/{project}/databases/{db}\") into its parent token values. It validates that each odd-positioned segment is non-empty and that each even-positioned segment exactly matches the expected prefix (e.g. \"projects/\"). This error is thrown when a segment fails that prefix/emptiness check, meaning the resource name's shape does not match the expected pattern.","triggerScenarios":"Calling GetProjectID, GetEnvironmentID, GetProjectIDWebhookID, GetProjectIDDatabaseGroupID, GetProjectIDAccessGrantID, or GetProjectIDQueryHistoryID with a resource name whose collection segments don't literally match the expected prefixes (e.g. \"project/abc\" instead of \"projects/abc\") or contain empty identifiers (\"projects//databases/x\").","commonSituations":"Hand-constructed resource names in scripts or tests, singular-vs-plural typos (\"project/\" vs \"projects/\"), names copied from another API's format, or names missing an ID after a prefix.","solutions":["Print the request name and compare each segment against the expected prefix (e.g. \"projects/\"); fix singular/plural or misspelled segments.","Ensure every collection ID segment is non-empty (no double slashes).","Use the library's own name-formatting helpers instead of building resource name strings by concatenation.","Validate the name with a regexp like ^projects/[^/]+/databases/[^/]+$ before passing it to the API."],"exampleFix":"// before\nGetProjectID(\"project/abc\")\n// after\nGetProjectID(\"projects/abc\")","handlingStrategy":"validation","validationCode":"var nameRe = regexp.MustCompile(`^projects/[^/]+(/databases/[^/]+)?$`)\nfunc isValidResourceName(name string) bool { return nameRe.MatchString(name) && !strings.Contains(name, \"//\") }\n// call GetProjectID only if isValidResourceName(name)","typeGuard":"func validTokens(name string) ([]string, bool) {\n  parts := strings.Split(name, \"/\")\n  if len(parts) < 2 || len(parts)%2 != 0 { return nil, false }\n  for i := 0; i < len(parts); i += 2 {\n    if parts[i] == \"\" || parts[i+1] == \"\" { return nil, false }\n  }\n  return parts, true\n}","tryCatchPattern":"tokens, err := common.GetProjectID(name)\nif err != nil {\n  if strings.Contains(err.Error(), \"invalid prefix\") {\n    // surface a 400 with the expected format, e.g. \"projects/{project}\"\n  }\n  return err\n}","preventionTips":["Always use the library's resource-name builder functions instead of string concatenation","Use plural collection segments exactly as defined (projects/, databases/, webhooks/)","Assert name shape with a regexp in tests","Never build names from unsanitized user input without trimming slashes"],"tags":["resource-name","parsing","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}