OtterMind/Chat2DB · warning · BusinessException

largeCellValue.tokenExpired

largeCellValue.tokenExpired

Error message

largeCellValue.tokenExpired

What it means

Thrown by DbLargeValueTokenServiceImpl.requireValid when tokenCache.get(id) returns null. LargeValueTokens live in an in-memory cache (tokenCache); a miss means the token was never issued, was evicted, or the process restarted and lost it. Resolves to 'This large value reference has expired. Refresh the result set and reopen this cell.' This is a recoverable, transient condition, not a hard failure.

Source

Thrown at chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/db/DbLargeValueTokenServiceImpl.java:83

                    cell.setUnsupportedReason(UNSUPPORTED_REASON_KEY);
                    continue;
                }
                LargeValueToken token = createToken(dataSourceId, databaseName, schemaName, tableName,
                        columnName, primaryKey, cell);
                tokenCache.put(token.getId(), token);
                cell.setLargeValueId(token.getId());
            }
        }
    }

    @Override
    public LargeValueToken requireValid(String id) {
        if (StringUtils.isBlank(id)) {
            throw new BusinessException("largeCellValue.tokenRequired");
        }
        LargeValueToken token = tokenCache.get(id);
        if (token == null) {
            throw new BusinessException("largeCellValue.tokenExpired");
        }
        if (Instant.now().isAfter(token.getExpiresAt())) {
            tokenCache.remove(id);
            throw new BusinessException("largeCellValue.tokenExpired");
        }
        validateOwner(token);
        return token;
    }

    private void cleanupExpired() {
        Instant now = Instant.now();
        tokenCache.entrySet().removeIf(entry -> now.isAfter(entry.getValue().getExpiresAt()));
    }

    private Map<Integer, Header> getPrimaryKeyHeaders(List<Header> headers) {
        Map<Integer, Header> primaryKeyHeaders = new LinkedHashMap<>();
        for (int i = 0; i < headers.size(); i++) {
            Header header = headers.get(i);

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Refresh the source result set so tokens are re-issued, then reopen the cell.
  2. If running multi-instance, pin large-value fetches to the instance that rendered the result set, or move token state to a shared store.
  3. Increase token TTL/cache capacity if evictions are frequent.
Defensive patterns

Strategy: retry

Try / catch

try {
    tokenService.requireValid(id);
} catch (BusinessException e) { // largeCellValue.tokenExpired
    // re-render the result set to re-issue tokens, then retry once
}

Prevention

When it happens

Trigger: Requesting a large value after the backend restarted (cache cleared); after the token TTL/capacity eviction ran; or with an id that was fabricated/never issued by this process. Any of these yield a null cache entry.

Common situations: Long-lived UI session where the user left a large cell open across a restart; horizontal scaling where the request lands on a different instance that does not hold the token; cache eviction under memory pressure.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/277e6e9437128c7e. Report an issue: GitHub.