{"record":{"id":"87607dc325bbff35","repo":"juanfont/headscale","slug":"tag-must-start-with-the-string-tag","errorCode":null,"errorMessage":"tag must start with the string 'tag:'","messagePattern":"tag must start with the string 'tag:'","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"hscontrol/api/v1/tags.go","lineNumber":11,"sourceCode":"package apiv1\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\n// ACL tag validation, shared by the node and pre-auth-key resources. These\n// reproduce the gRPC validateTag checks and messages.\nvar (\n\terrTagMissingPrefix = errors.New(\"tag must start with the string 'tag:'\")\n\terrTagNotLowercase  = errors.New(\"tag should be lowercase\")\n\terrTagHasSpaces     = errors.New(\"tags must not contain spaces\")\n)\n\n// validateTag reports whether an ACL tag is well formed: it must start with\n// \"tag:\", be lowercase, and contain no spaces.\nfunc validateTag(tag string) error {\n\tswitch {\n\tcase !strings.HasPrefix(tag, \"tag:\"):\n\t\treturn errTagMissingPrefix\n\tcase strings.ToLower(tag) != tag:\n\t\treturn errTagNotLowercase\n\tcase len(strings.Fields(tag)) > 1:\n\t\treturn errTagHasSpaces\n\tdefault:\n\t\treturn nil\n\t}\n}","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/api/v1/tags.go#L1-L29","documentation":"Error (with body and redirectURL returned) from doLoginURLWithClient when the response status is >= 400 (Bad Request and above), regardless of followRedirects. The login endpoint itself answered with a client or server error; body is logged and returned for diagnosis.","triggerScenarios":"GET on the login URL returning 4xx/5xx — e.g. 400 from a malformed auth request, 401 from an expired/invalid login session, or 500 from a control-server handler panic.","commonSituations":"Login URL already consumed or expired (interactive login attempted twice), headscale handler erroring (DB failure, policy error), or an OIDC provider returning 4xx for a bad callback.","solutions":["Inspect the logged response body — providers usually include a reason.","If 400/410-ish, the login URL may be single-use: regenerate it rather than replaying.","Check headscale logs for a 5xx stack trace and fix the underlying handler error.","Verify the hostname/network used to reach the login URL matches what issued it."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"body, redir, err := doLoginURLWithClient(hostname, loginURL, hc, true)\nif err != nil && strings.Contains(err.Error(), \"unexpected status code\") {\n    // 4xx/5xx: body is populated — surface it in the test failure message\n    t.Logf(\"login error body: %s\", body)\n}","preventionTips":["Treat login URLs as single-use; regenerate rather than replay.","Correlate with control-server logs when 5xx appears — it is a server-side defect.","Never silently swallow the returned body on this error."],"tags":["integration","http","oidc","login","status-code"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}