{"record":{"id":"09fa30139c7ea5f8","repo":"iflytek/astron-agent","slug":"bot-status-not-allow-offline","errorCode":"BOT_STATUS_NOT_ALLOW_OFFLINE","errorMessage":"BOT_STATUS_NOT_ALLOW_OFFLINE","messagePattern":"BOT_STATUS_NOT_ALLOW_OFFLINE","errorType":"error_code","errorClass":"BusinessException","httpStatus":null,"severity":"warning","filePath":"console/backend/hub/src/main/java/com/iflytek/astron/console/hub/strategy/publish/impl/MarketPublishStrategy.java","lineNumber":116,"sourceCode":"    @Transactional(rollbackFor = Exception.class)\n    public ApiResult<Object> offline(Integer botId, Object publishData, String currentUid, Long spaceId) {\n        log.info(\"Offlining bot from market: botId={}, currentUid={}, spaceId={}\", botId, currentUid, spaceId);\n\n        try {\n            // 1. Validate bot permission\n            int hasPermission = chatBotBaseMapper.checkBotPermission(botId, currentUid, spaceId);\n            if (hasPermission == 0) {\n                throw new BusinessException(ResponseEnum.BOT_NOT_EXISTS);\n            }\n\n            // 2. Query current publish status\n            BotPublishQueryResult queryResult = chatBotMarketMapper.selectBotDetail(botId, currentUid, spaceId);\n            Integer currentStatus = queryResult != null ? queryResult.getBotStatus() : null;\n            String currentChannels = queryResult != null ? queryResult.getPublishChannels() : null;\n\n            // 3. Validate offline conditions\n            if (currentStatus == null || !ShelfStatusEnum.isOnShelf(currentStatus)) {\n                throw new BusinessException(ResponseEnum.BOT_STATUS_NOT_ALLOW_OFFLINE);\n            }\n\n            Integer newStatus = ShelfStatusEnum.OFF_SHELF.getCode();\n            String newChannels = publishChannelService.updatePublishChannels(\n                    currentChannels, PublishChannelEnum.MARKET.getCode(), false);\n\n            // 4. Handle market data synchronization directly (offline operation)\n            handleBotMarketOffline(botId, currentUid, spaceId, newStatus, newChannels);\n\n            // 5. Publish event to trigger bot-type-specific operations if needed\n            eventPublisher.publishEvent(new BotPublishStatusChangedEvent(\n                    this, botId, currentUid, spaceId, \"OFFLINE\",\n                    currentStatus, newStatus, newChannels));\n\n            log.info(\"Market offline completed successfully: botId={}\", botId);\n            return ApiResult.success(null);\n\n        } catch (Exception e) {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/strategy/publish/impl/MarketPublishStrategy.java#L98-L134","documentation":"MarketPublishStrategy.offline() throws BOT_STATUS_NOT_ALLOW_OFFLINE when the bot cannot be taken off the market shelf. After selecting the bot's market record via chatBotMarketMapper.selectBotDetail, the code checks ShelveStatusEnum.isOnShelf(currentStatus); if the record is missing or the bot is not currently on-shelf, an offline operation is an invalid state transition and this error is thrown.","triggerScenarios":"Calling the market offline/publish API for a bot whose market record has a null status or whose status is not ON_SHELF (e.g. bot already off-shelf, never published to market, or removed).","commonSituations":"Double-clicking/submitting an offline request twice; attempting to unpublish a bot that was never market-published; concurrent offline by two admins so the second call sees OFF_SHELF; stale frontend page showing an on-shelf bot that has since been taken down.","solutions":["Reload the bot's market status before calling offline and only invoke it when ShelfStatusEnum.isOnShelf(status) is true","Handle the error as idempotent: if the bot is already off-shelf, treat the operation as a success/no-op on the caller side","Check for concurrent updates: re-fetch status after the error and confirm another operator did not already take the bot offline","Fix the data if status is unexpectedly null: verify chat_bot_market has a row for this botId/uid/spaceId and that it was inserted by the publish flow"],"exampleFix":"// before\npublishStrategy.offline(botId, data, uid, spaceId); // throws if not on shelf\n// after\nBotPublishQueryResult r = chatBotMarketMapper.selectBotDetail(botId, uid, spaceId);\nif (r != null && ShelfStatusEnum.isOnShelf(r.getBotStatus())) {\n    publishStrategy.offline(botId, data, uid, spaceId);\n}","handlingStrategy":"validation","validationCode":"BotPublishQueryResult r = chatBotMarketMapper.selectBotDetail(botId, uid, spaceId);\nif (r == null || !ShelfStatusEnum.isOnShelf(r.getBotStatus())) {\n    throw new IllegalStateException(\"Bot is not on the market shelf; offline not applicable\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    publishStrategy.offline(botId, data, uid, spaceId);\n} catch (BusinessException e) {\n    if (\"BOT_STATUS_NOT_ALLOW_OFFLINE\".equals(e.getCode())) { /* treat as already-offline no-op */ }\n    else throw e;\n}","preventionTips":["Refresh the bot's market status in the UI right before showing offline actions","Make offline calls idempotent and tolerate already-off-shelf states","Serialize concurrent publish/offline operations per bot (lock or optimistic versioning)"],"tags":["state-transition","publishing","marketplace"],"backgroundTag":"invalid-state-transition","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}