{"record":{"id":"2f6eea457734aa5f","repo":"vitessio/vitess","slug":"invalid-hint-s-expected-3-characters","errorCode":null,"errorMessage":"Invalid hint: %s, expected 3 characters","messagePattern":"Invalid hint: (.+?), expected 3 characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/schema/name.go","lineNumber":103,"sourceCode":"}\n\nfunc condenseUUID(uuid string) string {\n\tuuid = strings.ReplaceAll(uuid, \"-\", \"\")\n\tuuid = strings.ReplaceAll(uuid, \"_\", \"\")\n\treturn uuid\n}\n\n// isCondensedUUID answers 'true' when the given string is a condensed UUID, e.g.:\n// a0638f6bec7b11ea9bf8000d3a9b8a9a\nfunc isCondensedUUID(uuid string) bool {\n\treturn condensedUUIDRegexp.MatchString(uuid)\n}\n\n// generateGCTableName creates an internal table name, based on desired hint and time, and with optional preset UUID.\n// If uuid is given, then it must be in condensed-UUID format. If empty, the function auto-generates a UUID.\nfunc GenerateInternalTableName(hint string, uuid string, t time.Time) (tableName string, err error) {\n\tif len(hint) != 3 {\n\t\treturn \"\", fmt.Errorf(\"Invalid hint: %s, expected 3 characters\", hint)\n\t}\n\tif uuid == \"\" {\n\t\tuuid, err = CreateUUIDWithDelimiter(\"\")\n\t} else {\n\t\tuuid = condenseUUID(uuid)\n\t}\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif !isCondensedUUID(uuid) {\n\t\treturn \"\", fmt.Errorf(\"Invalid UUID: %s, expected condensed 32 hexadecimals\", uuid)\n\t}\n\ttimestamp := ToReadableTimestamp(t)\n\treturn fmt.Sprintf(\"_vt_%s_%s_%s_\", hint, uuid, timestamp), nil\n}\n\n// IsInternalOperationTableName answers 'true' when the given table name stands for an internal Vitess\n// table used for operations such as:","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/schema/name.go#L85-L121","documentation":"GenerateInternalTableName builds internal (_vt_...) table names as _vt_<hint>_<uuid>_<timestamp>_ and requires the hint to be exactly 3 characters, matching Vitess's internal naming convention. A shorter or longer hint makes the generated name unparseable by IsInternal*Table name parsers, so it is rejected up front.","triggerScenarios":"Calling schema.GenerateInternalTableName(hint, uuid, t) or generateGCTableName with a hint string whose len != 3, e.g. \"gc\" or \"xyz0\".","commonSituations":"Hand-written tooling or tests constructing GC table names with arbitrary hints; constants trimmed or truncated when passed through config; confusion between the 3-char GC state codes and full state names.","solutions":["Pass exactly a 3-character hint, e.g. the GC state code like \"rdg\", \"pur\", \"drp\".","Pad or map your desired label to a fixed 3-char code before calling.","If the hint is user-provided, validate len(hint) == 3 in your own code first."],"exampleFix":"// before\nname, err := schema.GenerateInternalTableName(\"gcname\", \"\", time.Now())\n// after\nname, err := schema.GenerateInternalTableName(\"gc_\", \"\", time.Now()) // exactly 3 chars","handlingStrategy":"validation","validationCode":"if len(hint) != 3 {\n    return fmt.Errorf(\"hint must be 3 chars, got %q\", hint)\n}","typeGuard":null,"tryCatchPattern":"name, err := schema.GenerateInternalTableName(hint, uuid, now)\nif err != nil {\n    return fmt.Errorf(\"cannot generate internal table name: %w\", err)\n}","preventionTips":["Use fixed 3-character constants for hints, never user input.","Validate hint length at the boundary where the hint originates.","Reuse existing GC state codes rather than inventing hints."],"tags":["schema","naming","validation"],"backgroundTag":"invalid-internal-table-name-hint","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}