{"record":{"id":"316eecff58d0fd39","repo":"grpc/grpc-go","slug":"expected-3-parts-in-token","errorCode":null,"errorMessage":"expected 3 parts in token","messagePattern":"expected 3 parts in token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/file_reader.go","lineNumber":94,"sourceCode":"\tif !ok { // no period found\n\t\treturn \"\", false\n\t}\n\tclaims, s, ok := strings.Cut(s, tokenDelim)\n\tif !ok { // only one period found\n\t\treturn \"\", false\n\t}\n\t_, _, ok = strings.Cut(s, tokenDelim)\n\tif ok { // three periods found\n\t\treturn \"\", false\n\t}\n\treturn claims, true\n}\n\n// extractExpiration parses the JWT token to extract the expiration time.\nfunc (r *jwtFileReader) extractExpiration(token string) (time.Time, error) {\n\tclaimsRaw, ok := extractClaimsRaw(token)\n\tif !ok {\n\t\treturn time.Time{}, fmt.Errorf(\"expected 3 parts in token\")\n\t}\n\tpayloadBytes, err := base64.RawURLEncoding.DecodeString(claimsRaw)\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"decode error: %v\", err)\n\t}\n\n\tvar claims jwtClaims\n\tif err := json.Unmarshal(payloadBytes, &claims); err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"unmarshal error: %v\", err)\n\t}\n\n\tif claims.Exp == 0 {\n\t\treturn time.Time{}, fmt.Errorf(\"no expiration claims\")\n\t}\n\n\texpTime := time.Unix(claims.Exp, 0)\n\n\t// Check if token is already expired.","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/jwt/file_reader.go#L76-L112","documentation":"Returned by extractClaimsRaw (via extractExpiration) when the token does not split into exactly three dot-separated parts (header.payload.signature). Without three parts the claims cannot be located, so parsing aborts. This is the most common JWT structural defect.","triggerScenarios":"The token is an opaque bearer string, a JSON document, a base64 blob, or a JWT with extra/missing dots; copy-paste truncation that dropped the signature segment.","commonSituations":"Writing an OAuth access token instead of an ID token; newline or trailing characters breaking the split; token truncated by a log/env var length limit.","solutions":["Confirm the file contains a single JWT with exactly two dots: printf '%s' \"$TOKEN\" | tr -cd '.' | wc -c should print 2.","Switch the token source to one that emits a signed JWT (ID token) rather than an opaque access token.","Strip any trailing newline/quotes from the file content."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"func isThreePartJWT(s string) bool {\n    return strings.Count(s, \".\") == 2\n}","typeGuard":"func isJWT(v string) bool {\n    s := strings.TrimSpace(v)\n    return strings.Count(s, \".\") == 2 && len(strings.Split(s, \".\")[1]) > 0\n}","tryCatchPattern":null,"preventionTips":["Before writing a token to the file, assert it has exactly two dots.","Use a JWT library to generate tokens rather than hand-assembling strings.","Strip trailing whitespace/newlines from the token."],"tags":["grpc","jwt","validation","parsing"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}