{"record":{"id":"b519745c4f81d0da","repo":"vxcontrol/pentagi","slug":"token-can-t-be-empty","errorCode":null,"errorMessage":"token can't be empty","messagePattern":"token can't be empty","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"backend/pkg/server/auth/auth_middleware.go","lineNumber":206,"sourceCode":"\t\tc.Set(\"cpt\", \"automation\")\n\t}\n\n\treturn authResultOk, nil\n}\n\nconst PrivilegeAutomation = \"pentagi.automation\"\n\nfunc (p *AuthMiddleware) tryProtoTokenAuthentication(c *gin.Context) (authResult, error) {\n\tauthHeader := c.Request.Header.Get(\"Authorization\")\n\tif authHeader == \"\" {\n\t\treturn authResultSkip, errors.New(\"token required\")\n\t}\n\n\tif !strings.HasPrefix(authHeader, \"Bearer \") {\n\t\treturn authResultSkip, errors.New(\"bearer scheme must be used\")\n\t}\n\ttoken := authHeader[7:]\n\tif token == \"\" {\n\t\treturn authResultSkip, errors.New(\"token can't be empty\")\n\t}\n\n\t// skip validation if using default salt (for backward compatibility)\n\tif p.globalSalt == \"\" || p.globalSalt == \"salt\" {\n\t\treturn authResultSkip, errors.New(\"token validation disabled with default salt\")\n\t}\n\n\t// try to validate as API token first (new format with JWT signing key)\n\tapiClaims, apiErr := ValidateAPIToken(token, p.globalSalt)\n\tif apiErr != nil {\n\t\treturn authResultFail, errors.New(\"token is invalid\")\n\t}\n\n\t// check token status and get privileges through cache\n\tstatus, privileges, err := p.tokenCache.GetStatus(apiClaims.TokenID)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/server/auth/auth_middleware.go#L188-L224","documentation":"After confirming the Authorization header starts with \"Bearer \", the middleware slices off the 7-character prefix and rejects the request when the remainder is empty. This means the client sent `Authorization: Bearer` (or `Bearer ` with only whitespace-stripped empty content) but no actual token value. The middleware cannot authenticate without a credential, so it returns authResultSkip for other mechanisms to attempt.","triggerScenarios":"Sending `Authorization: Bearer` or `Authorization: Bearer ` (trailing space, empty token), typically when a template variable holding the token is empty/unset, e.g. `Bearer ${API_TOKEN}` where API_TOKEN=\"\".","commonSituations":"CI/CD secret not injected so the token env var is empty; .env file missing the token; string interpolation in a config file silently producing \"Bearer \"; token deleted from settings and the client cached an empty value.","solutions":["Ensure the token variable is actually set before the request: check `echo -n \"${API_TOKEN}\" | wc -c` is non-zero.","Send `Authorization: Bearer <non-empty api token>` created from the API tokens settings UI.","Guard client code: skip the request or fail fast with a clear config error if the token is empty.","Regenerate an API token if the previous one was deleted from the database."],"exampleFix":"// before\nconst header = `Bearer ${process.env.API_TOKEN}`;\n\n// after\nif (!process.env.API_TOKEN) throw new Error(\"API_TOKEN is not set\");\nconst header = `Bearer ${process.env.API_TOKEN}`;","handlingStrategy":"validation","validationCode":"const token = process.env.API_TOKEN ?? \"\";\nif (!token.trim()) throw new Error(\"API token is empty; refusing to send request\");\nheaders[\"Authorization\"] = `Bearer ${token.trim()}`;","typeGuard":"function hasToken(t: unknown): t is string {\n  return typeof t === \"string\" && t.trim().length > 0;\n}","tryCatchPattern":"try {\n  return await client.request(opts);\n} catch (e) {\n  if (is401(e) && /can't be empty|token required/i.test(e.message)) {\n    throw new ConfigError(\"API token missing — check API_TOKEN secret\");\n  }\n  throw e;\n}","preventionTips":["Fail fast at startup when the token env var/secret is unset or empty.","Interpolate secrets with ${VAR:-} guards in CI and assert non-empty before deploy.","Trim tokens read from files to avoid whitespace-only values."],"tags":["http","authentication","go","empty-credential"],"backgroundTag":"empty-bearer-token","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}