{"record":{"id":"6268223df07409e8","repo":"semaphoreui/semaphore","slug":"read-option-w","errorCode":null,"errorMessage":"read option: %w","messagePattern":"read option: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/jwt.go","lineNumber":79,"sourceCode":"\t\t\tfmt.Fprintf(os.Stderr, \"jwt: invalid jwt_max_ttl %q, falling back to 24h: %v\\n\", Config.JWT.MaxTTL, err)\n\t\t}\n\t}\n\n\treturn jwt.SignerOptions{\n\t\tIssuer:     Config.JWT.Issuer,\n\t\tDefaultTTL: ttl,\n\t\tMaxTTL:     maxTTL,\n\t}\n}\n\n// loadOrCreateJWTKey returns the raw PEM bytes of the JWT signing key. It\n// reads the encrypted value from the database, decrypts it, and returns the\n// plaintext PEM. If no key exists yet it generates one, persists it, and\n// returns the plaintext PEM.\nfunc loadOrCreateJWTKey(store OptionStore) ([]byte, error) {\n\tstored, err := store.GetOption(jwtSigningKeyOption)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read option: %w\", err)\n\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 {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/util/jwt.go#L61-L97","documentation":"Returned by loadOrCreateJWTKey in util/jwt.go when the underlying OptionStore fails to read the jwt_signing_key option row from the database. It is a thin wrapping guard around store.GetOption: the %w carries the actual database error (connection failure, missing table, query error), and only the read step is affected — key decryption or generation happens in later branches.","triggerScenarios":"Thrown at util/jwt.go:79 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check the wrapped error for the concrete DB failure (connection refused, table missing, timeout)","Verify database connectivity and that migrations ran so the options table exists","Retry service startup after the database is reachable — a transient outage at boot is a common cause","Ensure the service's DB user has SELECT permission on the options table"],"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"}