Zie619/n8n-workflows · warning · HTTPException
Rate limit exceeded. Please try again later.
Error message
Rate limit exceeded. Please try again later.
What it means
A 429 from GET /api/workflows/{filename} when check_rate_limit(client_ip) returns False. The server keeps a per-IP sliding/fixed window of allowed requests; exceeding it within the window raises HTTPException(429) with this message. It protects the file-detail endpoint from hammering.
Source
Thrown at api_server.py:320
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Error searching workflows: {str(e)}"
)
@app.get("/api/workflows/{filename}")
async def get_workflow_detail(filename: str, request: Request):
"""Get detailed workflow information including raw JSON."""
try:
# Security: Validate filename to prevent path traversal
if not validate_filename(filename):
print(f"Security: Blocked path traversal attempt for filename: {filename}")
raise HTTPException(status_code=400, detail="Invalid filename format")
# Security: Rate limiting
client_ip = request.client.host if request.client else "unknown"
if not check_rate_limit(client_ip):
raise HTTPException(
status_code=429, detail="Rate limit exceeded. Please try again later."
)
# Get workflow metadata from database
workflows, _ = db.search_workflows(f'filename:"{filename}"', limit=1)
if not workflows:
raise HTTPException(
status_code=404, detail="Workflow not found in database"
)
workflow_meta = workflows[0]
# Load raw JSON from file with security checks
workflows_path = Path("workflows").resolve()
# Find the file safely
matching_file = None
for subdir in workflows_path.iterdir():View on GitHub (pinned to 94007c1445)
Solutions
- Wait for the rate-limit window to expire (typically seconds to a minute) before retrying.
- Cache detail responses client-side so revisiting a workflow does not re-hit the API.
- Reduce burstiness: debounce UI actions that trigger detail fetches.
- If you operate the server and the limit is genuinely too low, adjust the limit parameters in check_rate_limit()/its config rather than removing the check.
Example fix
// before
const data = await this.apiCall(`/workflows/${name}`);
// after (simple per-key client cache + backoff on 429)
if (!this._detailCache[name]) {
for (let attempt = 0; attempt < 3; attempt++) {
try { this._detailCache[name] = await this.apiCall(`/workflows/${name}`); break; }
catch (e) { if (!e.message.includes('429')) throw e; await new Promise(r => setTimeout(r, 1000 * (attempt + 1))); }
}
} Defensive patterns
Strategy: retry
Try / catch
try {
detail = await apiCall(`/workflows/${name}`);
} catch (e) {
if (String(e.message).includes('429')) {
await sleep(1500); // let the window reset
detail = await apiCall(`/workflows/${name}`);
} else throw e;
} Prevention
- Cache detail responses by filename.
- Debounce rapid detail-view navigation in the UI.
- Treat 429 as backoff signal, never as an error to surface to the user.
When it happens
Trigger: Rapidly opening many workflow detail views from one IP (each click fires at least one request), automated scraping of /api/workflows/<name>, or a page-load sequence that fans out several detail requests concurrently.
Common situations: Browsing the gallery quickly in a demo; running load tests against the API; sharing an IP (office NAT/proxy) so many users collectively exhaust the limit.
Related errors
AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15).
Data as JSON: /api/errors/0248b191ebe6bd66.
Report an issue: GitHub.