{"record":{"id":"4f86881285da37a4","repo":"kestra-io/kestra","slug":"unable-to-hold-the-lock-inside-the-configured-time","errorCode":null,"errorMessage":"Unable to hold the lock inside the configured timeout of {}","messagePattern":"Unable to hold the lock inside the configured timeout of (.+?)","errorType":"exception","errorClass":"LockException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/kestra/core/lock/LockService.java","lineNumber":72,"sourceCode":"        doInLock(category, id, DEFAULT_TIMEOUT, runnable);\n    }\n\n    /**\n     * Executes a Runnable inside a lock.\n     * If the lock is already taken, it will wait for at most the <code>timeout</code> duration.\n     * WARNING: for Elasticsearch, fencing via concurrency control should be used to avoid stale writes if a lock is taken over.\n     *\n     * @see #doInLock(String, String, Runnable)\n     *\n     * @param category lock category, ex 'executions'\n     * @param id identifier of the lock identity inside the category, ex an execution ID\n     * @param timeout how much time to wait for the lock if another process already holds the same lock\n     *\n     * @throws LockException if the lock cannot be hold before the timeout or the thread is interrupted.\n     */\n    public void doInLock(String category, String id, Duration timeout, Runnable runnable) {\n        if (!lock(category, id, timeout)) {\n            throw new LockException(\"Unable to hold the lock inside the configured timeout of \" + timeout);\n        }\n\n        try {\n            runnable.run();\n        } finally {\n            unlock(category, id);\n        }\n    }\n\n    /**\n     * Acquires the lock only if it is not held by another process at the time of invocation.\n     *\n     * @param category the category of the lock, e.g., 'executions'\n     * @param id the identifier of the lock within the specified category, e.g., an execution ID\n     * @return an optional {@link Disposable} to release the lock.\n     */\n    public Optional<Disposable> tryLock(String category, String id) {\n        return lock(category, id, Duration.ZERO) ? Optional.of(Disposable.of(() -> this.unlock(category, id))) : Optional.empty();","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/kestra-io/kestra/blob/823fada9274c4f9c251ea0a516460a4f7d958032/core/src/main/java/io/kestra/core/lock/LockService.java#L54-L90","documentation":"Thrown by LockService.doInLock when the lock cannot be acquired within the given timeout. lock(category, id, timeout) returns false (another holder kept it for the whole wait); doInLock then throws LockException with the timeout value. The runnable never executes. Used for mutual exclusion around critical sections keyed by category+id (e.g. executions, scheduling).","triggerScenarios":"doInLock(category, id, timeout, runnable) calls lock(...); lock() returns false because another process held the lock past the timeout; LockException(\"Unable to hold the lock inside the configured timeout of <timeout>\") is thrown before runnable.run().","commonSituations":"Concurrent executions/schedulers contending on the same lock id, a previous holder crashed without releasing (stale lock) and the backend lacks auto-expiry, the timeout is too short for the workload, or a long-running critical section under load serializes waiters.","solutions":["Increase the timeout passed to doInLock to match the expected critical-section duration.","Investigate prior holders — check logs/lock table for a stuck or orphaned lock.","Reduce the critical section size so locks are held for shorter windows.","If a stale lock is suspected, use the lock admin API/CLI to inspect and release orphaned locks.","Consider idempotent optimistic updates instead of a coarse lock."],"exampleFix":"// before\nlockService.doInLock(\"executions\", id, Duration.ofSeconds(5), runnable);\n// after\nlockService.doInLock(\"executions\", id, Duration.ofSeconds(30), runnable);","handlingStrategy":"retry","validationCode":"// Estimate required timeout from prior hold durations before locking\nDuration budget = expectedHoldDuration.multipliedBy(2);\nif (timeout.compareTo(budget) < 0) { /* warn: timeout may be too short */ }","typeGuard":null,"tryCatchPattern":"try {\n  lockService.doInLock(category, id, timeout, runnable);\n} catch (LockException e) {\n  if (e.getMessage().contains('configured timeout')) {\n    log.warn('Lock contention on {}/{}; consider widening timeout', category, id);\n  }\n  throw e;\n}","preventionTips":["Size the timeout to the longest expected critical section.","Keep critical sections short to reduce contention.","Monitor for stale locks and clear them via the admin API.","Prefer optimistic/CAS updates where the backend supports it."],"tags":["lock","concurrency","timeout","distributed","scheduling"],"backgroundTag":null,"analyzedSha":"823fada9274c4f9c251ea0a516460a4f7d958032","analyzedAt":"2026-08-14T06:15:17.947Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}