gotify/server · error · errCannotParseToken
cannot parse token
Error message
cannot parse token
What it means
errCannotParseToken is the sentinel error for tokens that cannot even be parsed as enhanced tokens — most commonly a token that does not start with the required 'gtfy' prefix (wrapped with %w so errors.Is matches). Unlike errInvalidToken (signature failure), this is a structural/format failure. The auth middleware special-cases it to return 401 instead of 500.
Source
Thrown at auth/token.go:23
"crypto/ed25519"
"crypto/rand"
"crypto/sha512"
"encoding/base64"
"errors"
"fmt"
"math/big"
"strconv"
"strings"
)
const (
maxTimestampDiffSeconds = 15 * 60
randomTokenLength = 22 // ~2^132 keyspace
)
var (
errInvalidToken = errors.New("invalid token")
errCannotParseToken = errors.New("cannot parse token")
errNoPrivateKey = errors.New("no private key")
tokenCharacters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_")
pluginPrefix = "P"
enhancedTokenPrefix = "gtfy"
randReader = rand.Reader
)
type EnhancedToken struct {
ident string
pubOrPrivKey []byte
timestamp int64
signature []byte
}
// PublicForm returns the a canonicalized representation of the public key.
func (c *EnhancedToken) PublicForm() string {
if c.timestamp != 0 || len(c.signature) != 0 {View on GitHub (pinned to 14bfc25627)
Solutions
- Verify the token string starts with 'gtfy' for enhanced tokens; if it starts with 'P' or is a plain random token, use the parser/handler for that token type or regenerate an enhanced token
- Generate a new enhanced token from the server and store it without modifications (no quotes, no trailing newline)
- Trim whitespace/newlines from the token in env/config before use
- Check errors.Is(err, errCannotParseToken) to distinguish format problems from signature problems when debugging
Example fix
// before
raw := "\"gtfyAbC...\"" // includes quotes
user, err := ParseEnhancedToken(raw)
// after
raw := strings.TrimSpace(strings.Trim(os.Getenv("GITEA_TOKEN"), \"\"))
if !strings.HasPrefix(raw, "gtfy") { /* regenerate enhanced token */ }
user, err := ParseEnhancedToken(raw) Defensive patterns
Strategy: try-catch
Validate before calling
if !strings.HasPrefix(strings.TrimSpace(token), "gtfy") {
return fmt.Errorf("value is not an enhanced token (must start with 'gtfy')")
} Type guard
func isEnhancedToken(s string) bool {
return strings.HasPrefix(strings.TrimSpace(s), "gtfy")
} Try / catch
user, err := ParseEnhancedToken(token)
if err != nil {
if errors.Is(err, errCannotParseToken) {
// format problem: wrong prefix, quotes, whitespace, or plain legacy token
return nil, fmt.Errorf("malformed token %q; regenerate an enhanced token", summarize(token))
}
return nil, err
} Prevention
- Trim whitespace and surrounding quotes when loading tokens from env/config
- Distinguish token types by prefix: 'gtfy' = enhanced, 'P' = plugin, otherwise legacy random token
- After server upgrades that change token format, regenerate stored tokens
- Never hardcode placeholder values like 'your-token-here' into production env files
When it happens
Trigger: Passing a random-plugin token ('P' prefix), a raw random token, an empty string, or an arbitrary string to ParseEnhancedToken or through auth middleware evaluate() where the token can't be parsed.
Common situations: Using an old-style plain token after the server switched to enhanced (signed) tokens; env var containing whitespace or a placeholder value; client library version mismatch producing a different token format; pasting a token with surrounding quotes.
Related errors
- invalid token
- %w: token must start with %s
- %w: token must have 2 or 4 fields separated by dots
- local authentication is disabled
- basic auth required
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/3677d355d3ef292b.
Report an issue: GitHub.