{"record":{"id":"6f398056a0439cba","repo":"crowdsecurity/crowdsec","slug":"failed-to-cast-machineid-to-string","errorCode":null,"errorMessage":"failed to cast machineID to string","messagePattern":"failed to cast machineID to string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/apiserver/controllers/v1/utils.go","lineNumber":52,"sourceCode":"\n\treturn false\n}\n\nfunc getMachineIDFromContext(ctx *gin.Context) (string, error) {\n\tclaims := jwt.ExtractClaims(ctx)\n\tif claims == nil {\n\t\treturn \"\", errors.New(\"failed to extract claims\")\n\t}\n\n\trawID, ok := claims[middlewares.MachineIDKey]\n\tif !ok {\n\t\treturn \"\", errors.New(\"MachineID not found in claims\")\n\t}\n\n\tid, ok := rawID.(string)\n\tif !ok {\n\t\t// should never happen\n\t\treturn \"\", errors.New(\"failed to cast machineID to string\")\n\t}\n\n\treturn id, nil\n}\n\nfunc (*Controller) AbortRemoteIf(option bool) gin.HandlerFunc {\n\treturn func(gctx *gin.Context) {\n\t\tif !option {\n\t\t\treturn\n\t\t}\n\n\t\tif isUnixSocket(gctx) {\n\t\t\treturn\n\t\t}\n\n\t\tincomingIP := gctx.ClientIP()\n\t\tif incomingIP != \"127.0.0.1\" && incomingIP != \"::1\" {\n\t\t\tgctx.JSON(http.StatusForbidden, gin.H{\"message\": \"access forbidden\"})","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/controllers/v1/utils.go#L34-L70","documentation":"The MachineID claim exists but is not a JSON string, so the assertion rawID.(string) fails and getMachineIDFromContext returns \"failed to cast machineID to string\". The comment notes this should never happen — it guards against a token whose machine-ID claim is a number, object, or other non-string type.","triggerScenarios":"A JWT whose machine ID claim was signed as a non-string JSON value (e.g. numeric ID or nested object), produced by custom or third-party token-issuing code rather than crowdsec's normal login flow.","commonSituations":"Hand-rolled token generators serializing the machine ID as a number; corrupted or manipulated tokens; test fixtures with claims built as map[string]interface{}{...: 42}.","solutions":["Re-authenticate the machine to obtain a token with a string machine-ID claim","Fix custom issuing code to store the machine ID as a string: fmt.Sprintf or the original string value","If you control the claim source, convert numbers with strconv before signing"],"exampleFix":"// before\nclaims[middlewares.MachineIDKey] = machineIntID\n// after\nclaims[middlewares.MachineIDKey] = fmt.Sprintf(\"%d\", machineIntID)","handlingStrategy":"type-guard","validationCode":"raw, ok := claims[middlewares.MachineIDKey]\nif !ok { return errors.New(\"missing machineID\") }\nif _, ok := raw.(string); !ok {\n    return fmt.Errorf(\"machineID claim must be a string, got %T\", raw)\n}","typeGuard":"s, ok := rawID.(string)\nif !ok { /* token minted with a non-string machine ID; reject */ }","tryCatchPattern":"machineID, err := getMachineIDFromContext(c)\nif err != nil {\n    c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{\"message\": \"malformed token\"})\n    return\n}","preventionTips":["Always sign the machine ID as a string in custom token-issuing code","Add a fixture test that decodes issued tokens and asserts claim types"],"tags":["jwt","type-assertion","claims","lapi"],"backgroundTag":"type-mismatch","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}