feat: improve OpenAI error handling and surfacing#178
Open
HardeepAsrani wants to merge 3 commits into
Open
Conversation
Validate API keys against the embeddings endpoint so a key is accepted only when it is genuinely usable (valid auth and available credits). Invalid keys are blocked, but account-level problems (no credits, rate limits) save the key and warn instead of blocking — new, unfunded accounts return a 429 even on the free moderation endpoint, so blocking on validation was wrong. Map OpenAI error codes to actionable, translatable messages from a single source of truth (centralised code-list constants), surface knowledge base indexing failures in the data UI with automatic retry, and show a dashboard service-error notice that is gated to the last 24 hours, cleared on the next successful request, and updates without a page reload via an apiFetch middleware. Refs Codeinwp/hyve#149, Codeinwp/hyve#199, Codeinwp/hyve#200
d28aec0 to
5fcb65e
Compare
Add DB_Table::ingest_document() as the shared tokenize -> moderate -> resolve post -> insert chunks -> embed path used by every data source; add_post() becomes a thin wrapper over it. process_post() now returns its result and accepts an $allow_retry flag so a caller can opt out of the background hyve_process_post retry. Posts add runs synchronously (retry_async=false): a failed embedding is surfaced immediately instead of being retried in the background.
Soare-Robert-Daniel
approved these changes
Jun 30, 2026
Qdrant only reacted to 403, so a deleted or paused cluster (404) failed silently on every chat while the Integrations page still showed it as connected. Route all Qdrant exceptions through a single handler that persists the error so it surfaces in the dashboard notice, and mark the connection inactive on 401/403/404 so the UI no longer reports a connection that no longer works. Add per-code messages so admins see the actual cause.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Improves how the plugin handles and surfaces OpenAI errors, addressing three support-driven issues:
What changed
Key validation (#149). Validation now hits the embeddings endpoint (the capability the plugin actually uses), so a key is accepted only when it's genuinely usable — valid auth and available credits. Real-world finding: a brand-new unfunded account returns
429even on the free moderation endpoint (with a null error code), so the old moderation-based check was both wrong and unhelpful. Embeddings returns a cleaninsufficient_quota. Invalid keys are blocked; account-level problems (no credits, rate limit) save the key and warn rather than blocking, so the user can proceed once they add credits.Actionable messages (#149/#199). A single source of truth maps OpenAI error codes to actionable, translatable copy (e.g. "…no available credits… add billing or upgrade to a paid plan"), consumed by both REST responses and the dashboard notice. Error-code lists are centralised into
OpenAI::AUTH_ERROR_CODES/OpenAI::PERSISTED_ERROR_CODESconstants so they can't drift.KB indexing failures (#199).
process_post()previously swallowed embedding/Qdrant failures, retried forever, andadd_post()still reported success. Failures are now classified: fatal errors (bad key, no credits, billing) stop immediately and mark the entry failed; transient errors (rate limit, network) retry with backoff up to a cap (5 attempts) then give up. The reason is surfaced both as an immediate warning toast at add time and as a status badge in the data list -- "…retried automatically" vs "…fix the problem and re-add" -- and clears on a successful attempt.Dashboard notice (#200). The service-error notice is gated to the last 24 hours, cleared on the next successful request, excludes transient rate-limits, and now updates without a page reload (store-backed
ErrorSection+ anapiFetchmiddleware that syncs from any/settingsresponse). The notice is reconciled with the saved key only after the save lands, so it can never reflect a key that wasn't stored.Notes
Test plan
The latest commit reworks how content is added to the Knowledge Base, not just error handling. Previously each of the four sources — Posts, Custom Data, Site Crawl, Sitemap — re-implemented the same tokenize → moderate → insert → embed flow with subtle differences. They now all funnel through a single method,
DB_Table::ingest_document(). (Pro callers move over in Codeinwp/hyve#230.)Because this touches every ingestion path, QA must re-test all four data sources end to end — this is a regression check on the whole "add to KB" surface, not only the OpenAI-error cases in the test plan above.
Also note one intentional behavior change: Posts "Add" is now fully synchronous — a failed embedding is surfaced immediately (warning toast) and is no longer retried in the background. Posts "Update" still retries via cron.
QA — confirm each source works as before (no regression)
For each of Posts, Custom Data, Site Crawl, Sitemap:
Run the above with both storage backends: default WordPress storage and Qdrant.