[BUG] Rate-limited providers silently lose analyzer coverage: max_concurrency=10 hardcoded, 429 batches dropped without retry, partial LLM pass not flagged
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 17.9k
- Forks
- 1.5k
- Avg merge
- 5d 10h
- Merged PRs (30d)
- 66
Description
Summary
On a rate-limited provider (typical free tier), arun_batches bursts up to a hardcoded max_concurrency=10 and drops any 429'd batch ("logged and omitted from the result") with no retry/backoff. There is no way to lower the concurrency or make the client respect the provider's RPM/TPM. The result is a scan that silently loses analyzer coverage while still emitting a full risk_assessment — including losing the security-critical semantic_security_discovery batch, so a genuinely malicious skill can read as clean.
This is distinct from #290 (provider unavailable): here the provider is available and answering, but throttling, and the partial result is not surfaced as such.
Evidence
Provider openai pointed at Gemini's OpenAI-compatible endpoint, free tier (5 RPM):
WARNING [skillspector.nodes.analyzers.semantic_security_discovery] semantic_security_discovery failed:
Error code: 429 - ... 'quotaId': 'GenerateRequestsPerMinutePerProjectPerModel-FreeTier', 'quotaValue': '5' ...
metadata.llm_calls_attempted: 4, llm_calls_succeeded: 3, yet risk_assessment.severity is emitted normally and issues: 0. On a known-malicious test skill (natural-language exfiltration of ~/.config/gh/hosts.yml), the dropped batch was exactly the analyzer that catches it — the scan reported LOW/clean.
Same pattern on Groq free tier: the ~33k-token semantic_security_discovery prompt exceeds the free TPM cap → 413 → dropped → clean-looking report.
Why the current design makes this invisible
max_concurrency=10is a fixed default inarun_batches; nothing threads a lower value or reads an env/flag. Ten parallel requests against a 5 RPM budget guarantee 429s.- 429s are caught per-batch and omitted; there is no retry honoring
Retry-After. llm_calls_succeeded < llm_calls_attemptedis recorded in metadata but the risk assessment does not treat a partial LLM pass as degraded, so downstream consumers see a normal verdict.
Requests (any subset helps)
- Configurable concurrency — an env var / CLI flag (e.g.
SKILLSPECTOR_MAX_LLM_CONCURRENCY) so users on rate-limited tiers can serialize. - Retry with backoff on 429/
RESOURCE_EXHAUSTED, honoringRetry-After, before giving up on a batch. - Surface incompleteness in the verdict: when any batch is dropped, mark the report degraded / refuse to assert a clean risk_assessment — same failure mode #290 guards against, one step earlier. A metadata flag like
llm_batches_dropped: Nwould let callers gate on it.
Happy to send a PR for (1) and (3) if the direction is agreeable.
Environment
SkillSpector (current main), SKILLSPECTOR_PROVIDER=openai against generativelanguage.googleapis.com/v1beta/openai/ and api.groq.com/openai/v1, free tiers.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the arun_batches entry point and trace how 429 or RESOURCE_EXHAUSTED responses are logged, omitted, and reflected in llm_calls_attempted and llm_calls_succeeded. Then follow risk_assessment construction to determine where configurable concurrency, retry behavior, and degraded verdict metadata belong; done means rate-limited batches are not silently presented as a complete clean scan.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100