grpc/grpc-go · error
token file access error
Error message
token file access error
What it means
errTokenFileAccess is returned by jwtFileReader.readToken() (credentials/jwt/file_reader.go:52) when os.ReadFile(r.tokenFilePath) fails. The jwt package reads a JWT from a file to supply per-RPC call credentials; if the underlying OS file read returns an error, the library wraps it with this sentinel. The wrapped error preserves the original OS error (e.g. 'no such file or directory', 'permission denied') via %w so errors.Is(err, errTokenFileAccess) can match it.
Source
Thrown at credentials/jwt/file_reader.go:32
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package jwt
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"time"
)
var (
errTokenFileAccess = errors.New("token file access error")
errJWTValidation = errors.New("invalid JWT")
)
// jwtClaims represents the JWT claims structure for extracting expiration time.
type jwtClaims struct {
Exp int64 `json:"exp"`
}
// jwtFileReader handles reading and parsing JWT tokens from files.
// It is safe to call methods on this type concurrently as no state is stored.
type jwtFileReader struct {
tokenFilePath string
}
// readToken reads and parses a JWT token from the configured file.
// Returns the token string, expiration time, and any error encountered.
func (r *jwtFileReader) readToken() (string, time.Time, error) {
tokenBytes, err := os.ReadFile(r.tokenFilePath)View on GitHub (pinned to 03255a9237)
Solutions
- Confirm the path resolves to a real readable file: run ls -l <tokenFilePath> and check it is a regular file readable by the process user.
- If the path comes from an env var, print os.Getenv() of that var at startup to verify it is set and not empty.
- In containers, verify the secret/projected volume is mounted at the same path the credentials config points to (check the mounted volume, not just the pod spec).
- Switch to an absolute path to avoid working-directory-dependent resolution failures.
Example fix
// before
creds := jwt.NewTokenFromFile(os.Getenv("MY_JWT_FILE")) // env unset -> ""
conn, _ := grpc.Dial(..., grpc.WithPerRPCCredentials(creds))
// after
p := os.Getenv("MY_JWT_FILE")
if fi, err := os.Stat(p); err != nil || fi.IsDir() {
log.Fatalf("token file %q not usable: %v", p, err)
}
creds := jwt.NewTokenFromFile(p) Defensive patterns
Strategy: validation
Validate before calling
// Validate the token file before constructing JWT credentials.
func validTokenFile(path string) error {
fi, err := os.Stat(path)
if err != nil {
return fmt.Errorf("token file %q: %w", path, err)
}
if fi.IsDir() {
return fmt.Errorf("token file %q is a directory", path)
}
// try a real read to catch permission issues
if _, err := os.ReadFile(path); err != nil {
return fmt.Errorf("token file %q unreadable: %w", path, err)
}
return nil
}
// usage
if err := validTokenFile(tokenPath); err != nil { log.Fatal(err) } Try / catch
// jwt credentials surface read errors via GetRequestMetadata at call time;
// detect the sentinel and surface it clearly.
md, err := creds.GetRequestMetadata(ctx)
if err != nil {
if errors.Is(err, errTokenFileAccess) {
// token file is missing/unreadable; check path & permissions
}
} Prevention
- Use absolute paths for the token file to avoid working-directory dependence.
- In containers, validate the mounted secret volume path at process start.
- Centralize credential construction in one function that stat-checks files first.
When it happens
Trigger: Constructing or using JWT-based per-RPC credentials that point at a tokenFilePath which os.ReadFile cannot open — path missing, file unreadable, path is a directory, or the configured path string is empty/garbage. It surfaces at the first readToken() call (e.g. when GetRequestMetadata is invoked for the first RPC).
Common situations: Misconfigured GOOGLE_APPLICATION_TOKEN_FILE / token path env var; in Kubernetes/Docker the service-account JWT volume or secret not mounted at the expected path; wrong working directory making a relative path resolve incorrectly; file owned by another user with mode 0600; symlink pointing to a nonexistent target.
Related errors
- empty token_exchange_service_uri in options
- required field SubjectTokenPath is not specified
- required field SubjectTokenType is not specified
- missing fallback credentials
- ClientHandshake() is not supported for server credentials
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/2eafd600d5ecddac.
Report an issue: GitHub.