zylon-ai/private-gpt · critical · ValueError

Visibility timeout should be greater than hard time limit

Error message

Visibility timeout should be greater than hard time limit

What it means

Config-validation ValueError from CelerySettings.validate_config: when hard_time_limit and visibility_timeout are both set, visibility_timeout must be >= hard_time_limit. Even a task that runs all the way to its hard kill must stay inside the Redis visibility window, otherwise Redis redelivers it while the first execution is still being killed, causing duplicate side effects.

Source

Thrown at private_gpt/settings/settings.py:1186

            if self.hard_time_limit and self.soft_time_limit > self.hard_time_limit:
                raise ValueError(
                    "Soft time limit should be less than or equal to hard time limit"
                )

            if (
                self.visibility_timeout
                and self.visibility_timeout < self.soft_time_limit
            ):
                raise ValueError(
                    "Visibility timeout should be greater than soft time limit"
                )

        if self.hard_time_limit:
            if (
                self.visibility_timeout
                and self.visibility_timeout < self.hard_time_limit
            ):
                raise ValueError(
                    "Visibility timeout should be greater than hard time limit"
                )

        return True


class RedisSettings(BaseModel):
    host: str = Field(description="Redis host")
    username: str | None = Field(default=None, description="Redis username")
    password: str | None = Field(default=None, description="Redis password")
    database: str | None = Field(default=None, description="Redis name")

    @property
    def url(self) -> str:
        """Database URL without the protocol."""
        username_path = f"{self.username}" if self.username else ""
        password_path = f"{self.password}" if self.password else ""
        credentials = (

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Set visibility_timeout above hard_time_limit (rule of thumb: hard limit plus several minutes of headroom).
  2. Or reduce hard_time_limit if such long tasks are a misconfiguration.
  3. Keep the full ordering consistent: visibility_timeout >= hard_time_limit >= soft_time_limit.
  4. Restart the API and all workers so the new window takes effect.

Example fix

# before
celery:
  broker_mode: redis
  visibility_timeout: 300
  hard_time_limit: 600
# after
celery:
  broker_mode: redis
  visibility_timeout: 900
  hard_time_limit: 600
Defensive patterns

Strategy: validation

Validate before calling

hard, vis = cfg['celery'].get('hard_time_limit'), cfg['celery'].get('visibility_timeout')
if hard and vis:
    assert vis >= hard, f'visibility_timeout ({vis}) must be >= hard_time_limit ({hard})'

Type guard

const visibilityCoversHard = (c: { visibility_timeout?: number; hard_time_limit?: number }): boolean =>
  c.visibility_timeout == null || c.hard_time_limit == null || c.visibility_timeout >= c.hard_time_limit;

Prevention

When it happens

Trigger: Redis broker with visibility_timeout: 120 and hard_time_limit: 600; introducing generous hard limits (e.g. for batch jobs) on a config whose visibility_timeout was tuned for quick tasks; visibility_timeout left at a low default from a template.

Common situations: Workload mix shifting from short to long tasks; ops adding hard_time_limit for safety without revisiting broker settings; staging/prod env var divergence.

Understand the failure class

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/fb54ccc2811cf25a. Report an issue: GitHub.