alibaba/open-code-review · error
OCR_LLM_TIMEOUT must be an integer (seconds): %w
Error message
OCR_LLM_TIMEOUT must be an integer (seconds): %w
What it means
OCR_LLM_TIMEOUT is interpreted strictly as an integer number of seconds. parseTimeoutEnv uses strconv.Atoi on the trimmed value; anything non-numeric ("30s", "2m", "") beyond empty produces this wrapped error. Like other global overrides, it fails fast before credential prompts.
Source
Thrown at internal/llm/resolver.go:210
ep.ExtraHeaders[key] = value
}
}
}
return ep
}
// parseTimeoutEnv reads and validates the OCR_LLM_TIMEOUT environment variable.
// Returns the parsed duration and true if set, or 0 and false if unset/empty.
// Returns an error for invalid values (non-integer, negative, overflow) to give
// the user clear feedback instead of silently falling back to the default.
func parseTimeoutEnv() (time.Duration, bool, error) {
raw := strings.TrimSpace(os.Getenv(envOCRLLMTimeout))
if raw == "" {
return 0, false, nil
}
sec, err := strconv.Atoi(raw)
if err != nil {
return 0, false, fmt.Errorf("OCR_LLM_TIMEOUT must be an integer (seconds): %w", err)
}
d, err := validateTimeoutSec(sec)
if err != nil {
return 0, false, fmt.Errorf("OCR_LLM_TIMEOUT: %w", err)
}
return d, true, nil
}
// validateTimeoutSec converts a config-file timeout (in seconds) to time.Duration.
// Returns 0 for zero input (use default). Rejects negative values and overflow.
func validateTimeoutSec(sec int) (time.Duration, error) {
if sec == 0 {
return 0, nil
}
if sec < 0 {
return 0, fmt.Errorf("timeout_sec must be non-negative, got %d", sec)
}
// Guard against overflow: time.Duration is int64 nanoseconds.View on GitHub (pinned to 5cf97d0d15)
Solutions
- Set OCR_LLM_TIMEOUT to a plain integer of seconds, e.g. 120 instead of "2m"
- Remove the unit suffix and any surrounding characters
- Unset the variable to use the client default (5 minutes)
Example fix
// before export OCR_LLM_TIMEOUT="90s" // after export OCR_LLM_TIMEOUT=90
Defensive patterns
Strategy: validation
Validate before calling
raw := strings.TrimSpace(os.Getenv("OCR_LLM_TIMEOUT"))
if raw != "" {
if _, err := strconv.Atoi(raw); err != nil {
return fmt.Errorf("OCR_LLM_TIMEOUT must be plain seconds integer, got %q", raw)
}
} Try / catch
if err != nil && strings.Contains(err.Error(), "OCR_LLM_TIMEOUT must be an integer") {
return fmt.Errorf("export OCR_LLM_TIMEOUT=90 instead of %q", os.Getenv("OCR_LLM_TIMEOUT"))
} Prevention
- Use bare integers (seconds), never "30s"/"2m" duration strings
- Prefer OCR_LLM_TIMEOUT=0 or unset over unit-style values
- Document the variable in team onboarding to prevent copy-paste of Go duration syntax
When it happens
Trigger: Setting OCR_LLM_TIMEOUT to a duration-style string ("30s", "1m30s"), a float ("2.5"), or any non-integer while ResolveEndpointWithOptions runs parseEnvOverrides.
Common situations: Users copying Go duration syntax from other tools; assuming the variable accepts "30s" because many CLIs do; a locale or whitespace artifact is handled by TrimSpace, so the usual cause is a unit suffix.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/e19bb83f1514edc8.
Report an issue: GitHub.