{"record":{"id":"8928a5db8e74acb3","repo":"semaphoreui/semaphore","slug":"persist-signing-key-w","errorCode":null,"errorMessage":"persist signing key: %w","messagePattern":"persist signing key: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/jwt.go","lineNumber":98,"sourceCode":"\t}\n\n\tif stored != \"\" {\n\t\treturn decryptJWTKey(stored)\n\t}\n\n\t// No key in DB yet\n\tpemBytes, err := jwt.GenerateKeyPEM()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tencrypted, err := encryptJWTKey(pemBytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := store.SetOption(jwtSigningKeyOption, encrypted); err != nil {\n\t\treturn nil, fmt.Errorf(\"persist signing key: %w\", err)\n\t}\n\n\treturn pemBytes, nil\n}\n\n// CheckJWTSigningKey classifies the stored JWT signing key for `vault check`:\n// \"\" (none), \"active:<id>\", \"rekey pending:<id>\", \"legacy (no id)\", or\n// \"MISSING KEY <id>\". Read-only.\nfunc CheckJWTSigningKey(store OptionStore) (slot string, err error) {\n\tstored, err := store.GetOption(jwtSigningKeyOption)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read jwt signing key option: %w\", err)\n\t}\n\tif stored == \"\" {\n\t\treturn \"\", nil\n\t}\n\treturn Config.classifyOptionSecret(stored), nil\n}","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/util/jwt.go#L80-L116","documentation":"Returned by loadOrCreateJWTKey in util/jwt.go at first-run initialisation: a fresh ECDSA P-256 key was generated and encrypted, but store.SetOption failed to persist it under the jwt_signing_key option. The %w wraps the database write error; the in-memory key is usable for this run, but without persistence the next start generates a different key and all tokens signed now become invalid.","triggerScenarios":"Thrown at util/jwt.go:98 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check the wrapped DB error — write permission on the options table or a connectivity blip at first boot are typical","Retry startup; if a later attempt stores successfully the key becomes stable","Verify the service's DB user has INSERT/UPDATE permission on the options table","If a different key was generated on a subsequent start, expect previously issued JWTs to fail verification and re-issue them"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}