Troubleshooting
Resolve common PatchPatrol failures quickly using exit codes, stderr signals, and artifact metadata.
Troubleshooting
This page stays on the supported GitLab artifact-first PatchPatrol path. Use it when setup, diff planning, provider calls, policy gates, or GitLab delivery break and you need one concrete next move.
Use this order for every failure:
- Confirm the exit code and first failing stderr line.
- Open artifacts at
AI_REVIEW_OUTPUT_DIR(default:.ai-review). - Check
.ai-review/ai-review.jsonfor the matchingmeta.*section.
Operational Diagnostics
Every completed review ends with a bounded Operational Diagnostics section in the job console. It describes the current execution; it is not part of the persistent GitLab Review Summary.
Normal output
Normal output is the routine handoff for developers and operators. A sanitized example looks like this:
Operational Diagnostics:
Review Outcome: Attention needed
Review Coverage: Full (4/4 validated chunks)
Finding Counts: blocker=0 high=1 medium=2 suggestions=1 total=4
Provider / Model: openai / review-model
Total Duration: 42.80s
Total Token Usage: calls=4 usage=4/4 prompt=18400 completion=2200 total=20600
GitLab Delivery: updated (current, summary+inline)
Artifact Paths: .ai-review/ai-review.json, .ai-review/ai-review.md, .ai-review/ai-review.html
Exit Reason: SUCCESS (status 0)The values come from the run that just finished. Use the artifact paths to open its Run Reports, and use the typed exit reason before changing configuration.
Verbose output
Set VERBOSE=1 when normal output identifies the failing area but you need
deeper run evidence. The console groups these rows under
Verbose Diagnostics:. The verbose tier adds resolved configuration, phase
outputs and next actions, per-phase and per-call usage, chunk-plan and
execution detail, incremental-review and GitLab delivery reason codes,
state-recovery signals, and rerun side-effect counts.
Debug output
Set LOG_LEVEL=DEBUG for allowlisted structural troubleshooting fields such as
report-section presence, metadata-key presence, finding counts, and bounded
reason-code context. The additional rows appear under Debug Diagnostics:.
Debug includes the verbose tier; it does not unlock raw runtime content.
No console tier prints credentials, API tokens, raw prompts, source excerpts, diffs, or raw provider request/response payloads. Use the typed reason codes and Run Report metadata instead of adding ad hoc secret-bearing logging.
Symptom Map
| Symptom | Likely cause | Fix |
|---|---|---|
CONFIG_ERROR: ... or exit code 2 | Core config validation failed before run started | Check env var names, booleans, and numeric values in Configuration Reference. |
LICENSE_ERROR[...] or exit code 12 | Self-Hosted License evaluation blocked real-provider review work | Run ai-review license inspect --json, then fix the reported LICENSE_* reason before rerunning. |
OPENAI_CONFIG_ERROR[...] or OPENAI_CONFIG_* with exit 4 | AI_REVIEW_PROVIDER=openai is missing or using invalid settings | Set a valid OPENAI_BASE_URL, remove unsupported OpenAI-only knobs, and rerun readiness checks. |
DIFF_CONTEXT_ERROR[...] or exit 4 | Diff context could not be resolved | Run ai-review run --mode auto --dry-run, then fix --mode, --base-ref, or --head-ref. |
DIFF_CONTEXT_ERROR[MERGE_BASE_NOT_FOUND] or GITLAB_DIFF_BASE_REF_NOT_FOUND | Branch/MR mode could not resolve a safe reviewed diff base | Fetch enough target/source history or GitLab MR diff refs, then rerun. |
meta.incremental_review.status=full on an MR rerun | Incremental provider scope was unsafe or unavailable | Inspect meta.incremental_review.reason_codes; full MR review is the safe fallback. |
DIFF_EXTRACTION_ERROR[...] with MAX_FILES_EXCEEDED, MAX_DIFF_BYTES_EXCEEDED, or INVALID_LIMIT (exit 5) | Diff scope is too large or the limit set is invalid | Raise the right limit or narrow the review scope. |
PROVIDER_ERROR[...] or OPENAI_API_ERROR[...] / OLLAMA_API_ERROR[...] | Provider endpoint, auth, or transport failure | Check provider reachability, model availability, and retry budget. |
TRUST_ALLOWLIST_ENDPOINT_MISSING, TRUST_ALLOWLIST_BLOCKED_ENDPOINT | A configured provider allowlist blocked remote calls before review started | Align the provider base URL with AI_REVIEW_PROVIDER_ALLOWLIST_BASE_URLS, then rerun. |
EXIT_REASON[FAIL_ON_*] or EXIT_REASON[FAIL_ON_PARTIAL_COVERAGE] with exit 10 | Review gate tripped on findings or omitted chunks | Review findings and meta.limits, then adjust policy only if your team intends to change it. |
GITLAB_NOTE_ERROR[...] with exit 9 | GitLab MR feedback failed after artifacts were written | Open the artifacts first, then repair GitLab delivery. |
Exit code 6 (ARTIFACT_ERROR) | Report write failed before completion | Check that .ai-review exists and is writable by the runner. |
Exit semantics you can use to triage
| Exit | Meaning |
|---|---|
2 | Config validation failed (CONFIG_ERROR[...]). |
3 | Provider API transport/response failure (OPENAI_API_ERROR[...], OLLAMA_API_ERROR[...]). |
4 | Diff-context failfast or OpenAI config validation (DIFF_CONTEXT_ERROR[...], OPENAI_CONFIG_ERROR[...]). |
5 | Diff extraction failfast (DIFF_EXTRACTION_ERROR[...]). |
6 | Artifact persistence failure (ARTIFACT_ERROR[...]). |
7 | Provider runtime failure (PROVIDER_ERROR[...]). |
8 | Provider output validation fallback (EXIT_REASON[LLM_OUTPUT_INVALID]). |
9 | GitLab MR note/feedback failure (GITLAB_NOTE_ERROR[...]). |
10 | Review gate triggered by configured policy. |
11 | Trust gate blocked execution before review. |
12 | Self-Hosted License blocked real-provider review work before provider calls. |
Recovery lanes
Access and readiness failures
Use this lane when the run never really starts.
Common signals:
CONFIG_ERROR: ...OPENAI_CONFIG_ERROR[...]TRUST_ALLOWLIST_ENDPOINT_MISSINGTRUST_ALLOWLIST_BLOCKED_ENDPOINT
What to check:
- Run
ai-review test --chat --readiness-jsonin the same environment the job uses for artifact-only provider/config readiness. - Provide
PATCHPATROL_LICENSEorPATCHPATROL_LICENSE_FILEfor real-provider chat probes; license failures exit12before provider calls. - Add
--semantic-readinessonly when the failing rollout enablesdiff+semantic. - Run
ai-review test --gitlab-readiness --readiness-jsononly when troubleshooting MR feedback delivery with the required GitLab token/context. - Confirm the admin-owned handoff is complete: provider endpoint, model, GitLab project access, and merge request pipeline wiring.
- When
AI_REVIEW_PROVIDER=openai, verifyOPENAI_BASE_URL,OPENAI_API_KEYwhen required, and remove unsupported OpenAI-only knobs such asAI_REVIEW_NUM_CTX. - When allowlist trust-gate signals appear, normalize the actual provider base URL and set
AI_REVIEW_PROVIDER_ALLOWLIST_BASE_URLSto that exact value, or leave the variable unset to skip endpoint allowlist enforcement.
Next action: Fix the missing prerequisite first, then return to Access and roles or Configuration Reference before rerunning the review.
Self-Hosted License failures
Use this lane when stderr contains LICENSE_ERROR[...], readiness metadata uses
the license failure category, or the command exits 12.
Inspect the configured runtime source without running review work or calling a provider:
ai-review license inspect --jsonThe JSON status is the License Status:
| Status | Meaning |
|---|---|
valid | Signature, claims, feature, and validity window are accepted; inspection exits 0. |
missing | No license source is configured. |
invalid | The source, file, token format, or signature is not usable. |
expired | The trusted validity window has ended. |
not_yet_valid | The trusted validity window has not started. |
unsupported | The signing key, product, license type, or required feature is unsupported. |
Use the first reason_codes entry for the concrete recovery action:
| Reason code | Recovery |
|---|---|
LICENSE_MISSING | Configure exactly one of PATCHPATROL_LICENSE or PATCHPATROL_LICENSE_FILE. |
LICENSE_SOURCE_AMBIGUOUS | Remove one source; PatchPatrol does not choose between two configured licenses. |
LICENSE_FILE_READ_FAILED | Fix the mounted file path and runner/container read permissions. |
LICENSE_FORMAT_INVALID | Replace the malformed or unsupported Signed License payload. |
LICENSE_SIGNATURE_INVALID | Request a correctly signed replacement from PatchPatrol support. |
LICENSE_KEY_UNKNOWN | Use a license issued for a key_id supported by the installed runtime release. |
LICENSE_EXPIRED | Renew the license; Self-Hosted License v1 has no automatic post-expiry grace period. |
LICENSE_NOT_YET_VALID | Check the license start time and runner clock, or use the license after its validity window begins. |
LICENSE_PRODUCT_UNSUPPORTED | Request a license issued for the patchpatrol product. |
LICENSE_TYPE_UNSUPPORTED | Request a self_hosted license. |
LICENSE_FEATURE_UNSUPPORTED | Request a license that includes the review feature. |
License evaluation fails before provider-backed work. When a blocked run can
write artifacts, inspect meta.license; the raw Signed License and signature
are never emitted. Empty findings on a license-blocked artifact mean no
provider review ran, so empty findings do not mean the diff or codebase is clean.
Next action: Get ai-review license inspect --json to return valid before
rerunning a real-provider review or test --chat probe.
Repository setup and diff resolution failures
Use this lane when PatchPatrol starts but cannot decide what to review.
Common signals:
DIFF_CONTEXT_ERROR[INVALID_MODE_COMBINATION]DIFF_CONTEXT_ERROR[MISSING_MR_BASE_REF]DIFF_CONTEXT_ERROR[MERGE_BASE_NOT_FOUND]DIFF_CONTEXT_ERROR[GITLAB_DIFF_BASE_REF_NOT_FOUND]DIFF_EXTRACTION_ERROR[NO_WORKING_TREE_CHANGES]DIFF_EXTRACTION_ERROR[NO_STAGED_CHANGES]DIFF_EXTRACTION_ERROR[MAX_DIFF_BYTES_EXCEEDED]
What to check:
- Run
ai-review run --mode auto --dry-runto print the resolved review context before a full rerun. - Confirm the GitLab runner can fetch the target branch used as the merge base.
- For branch/MR runs, inspect
meta.diff_base.strategyin.ai-review/ai-review.json:merge-basemeans PatchPatrol reviewed from the resolved merge base, whilegitlab-diff-base-shameans it used GitLab's MR diff base SHA. - For GitLab MR reruns, inspect
meta.incremental_review:status=incrementalmeans the provider reviewed the delta since the prior PatchPatrol-reviewed head SHA, whilestatus=fullrecords the full-review fallback reason. - If
meta.diff_base.statusiserror, readmeta.diff_base.diagnostic, then fetch enough source/target history forgit merge-base <base> <head>or GitLab's diff-base SHA to resolve. - Inspect
meta.limits.chunk_omitted_reasons,meta.limits.chunk_omitted_count, and the configured limits when large files or many files were skipped. - Increase only the limit that matches the omission signal:
AI_REVIEW_MAX_FILES,AI_REVIEW_MAX_DIFF_BYTES,AI_REVIEW_MAX_CHUNKS, orAI_REVIEW_MAX_FILE_DIFF_BYTES.
Next action: Get the diff plan into a known-good state first, then continue with First review output once artifacts are present again.
Provider, trust gate, and review-start failures
Use this lane when diff planning worked but the remote review step failed or never cleared the trust boundary.
Common signals:
PROVIDER_ERROR[...]OPENAI_API_ERROR[...]OLLAMA_API_ERROR[...]EXIT_REASON[LLM_OUTPUT_INVALID]TRUST_ALLOWLIST_*
What to check:
- Run
ai-review test --chat --readiness-jsonand read the finalreadiness.metadataline before changing provider settings. - Use readiness
failure_categoriesas the first branch:configurationmeans required settings are missing or invalid,licensemeans the Self-Hosted License is missing or not trusted,trust_gate_blockedmeans the allowlist/redaction boundary stopped the call,authenticationmeans credentials or scopes failed,model_unavailablemeans the endpoint did not list the configured model,network_connectivitymeans DNS/routing/proxy/endpoint reachability failed,structured_output_unsupportedmeans schema-style response formatting is not accepted by the endpoint, andtransport_retry_exhaustedmeans retryable transport failures persisted through the configured attempt budget. - In
.ai-review/ai-review.json, inspectmeta.provider_runtime.status,meta.provider_runtime.category, andmeta.provider_runtime.reason_codes. - Confirm the configured model exists at the endpoint and the runner can reach
OLLAMA_HOSTorOPENAI_BASE_URL. - If
TRANSPORT_RETRY_EXHAUSTEDappears, increaseAI_REVIEW_TRANSPORT_RETRY_MAX_ATTEMPTSonly when transient network failures are normal for that CI path. - If
meta.limits.structured_output_reason_codesincludesSTRUCTURED_OUTPUT_JSON_SCHEMA_UNSUPPORTED_FALLBACK, the OpenAI-compatible endpoint rejected schema-mode output. KeepAI_REVIEW_STRUCTURED_OUTPUT_MODE=jsonfor that endpoint, or enablejson_schema/autoonly after endpoint compatibility is verified. - If the run stopped at
EXIT_REASON[LLM_OUTPUT_INVALID], inspectmeta.limits.error_flagsand reduce diff pressure before retrying. - If allowlist trust-gate signals appear before artifacts exist, return to Admin Quickstart and confirm
AI_REVIEW_PROVIDER_ALLOWLIST_BASE_URLSexactly matchesOPENAI_BASE_URLorOLLAMA_HOST, or leave the allowlist unset. - If a trust-gate failure writes artifacts, read it as a blocked review:
meta.trust_gate.statusshould explain the fail-closed reason, no provider call was made, andmeta.usage_ledger.calls.call_countmay be0with zero prompt/completion/total tokens. - Do not treat
findings: []on an exit11trust-gate-blocked run as a clean review signal. It means PatchPatrol stopped before provider execution, so no merge request findings were produced.
Next action: Repair the provider or trust-boundary failure first, then re-run readiness checks and only then start another full review.
Policy and partial-coverage failures
Use this lane when PatchPatrol completed enough work to produce findings or omission data, but your configured gate still failed the run.
Common signals:
EXIT_REASON[FAIL_ON_BLOCKER]EXIT_REASON[FAIL_ON_HIGH]EXIT_REASON[FAIL_ON_MEDIUM]EXIT_REASON[FAIL_ON_PARTIAL_COVERAGE]
What to check:
- Read
ai-review.mdfirst to confirm whether the gate was triggered by validated findings or by omitted chunks. - In
.ai-review/ai-review.json, inspectmeta.limits.chunk_omitted_count,meta.limits.chunk_omitted_reasons, and any relatederror_flags. - Confirm whether the current policy is intentional:
AI_REVIEW_FAIL_ONsets the severity gate andAI_REVIEW_FAIL_ON_PARTIAL_COVERAGE=trueturns omissions into a hard failure. - Treat policy changes as a team decision, not an emergency workaround.
Next action: Decide whether to fix the findings, increase review coverage, or intentionally change policy in Configuration Reference.
GitLab delivery and artifact-location failures
Use this lane when the review artifacts exist or should exist, but developers cannot find them or GitLab feedback delivery failed after artifact writing.
Common signals:
GITLAB_NOTE_ERROR[GITLAB_TIMEOUT]GITLAB_NOTE_ERROR[NETWORK_ERROR]GITLAB_NOTE_ERROR[GITLAB_HTTP_401],GITLAB_HTTP_403,GITLAB_HTTP_404GITLAB_NOTE_ERROR[MISSING_*]FEEDBACK_DRAFT_SUPPRESSED
What to check:
- Open the job artifacts first and confirm
.ai-review/ai-review.html,.ai-review/ai-review.md, and.ai-review/ai-review.jsonexist. - In
.ai-review/ai-review.json, inspectmeta.feedback.delivery.statusandmeta.feedback.delivery.reason_codes. - Confirm the job publishes
.ai-review/**as GitLab artifacts and thatAI_REVIEW_OUTPUT_DIRmatches the published path. - If the run is still artifact-only, remember that missing MR notes are expected; the artifacts remain the source of truth.
Next action: Recover the artifact path first, then fix MR feedback delivery only if your team has already moved beyond the artifact-only baseline.
Suspicious throughput numbers
Use this lane when a report shows unexpectedly high or low token-per-second values, especially on local Ollama runs.
What to check:
- Start with
Generation tok/s. For Ollama, this is the model decode-speed metric because it uses generated tokens over provider generation time. - Treat
Wall total tok/sas user-visible provider-call throughput, not decode speed. It divides total prompt plus completion tokens by PatchPatrol wall time, so a large prompt and short completion can look much faster than the model is actually generating tokens. - Use
Prompt tok/sto diagnose prompt ingestion pressure,Load time (s)for cold model loads, andOverhead (s)for Docker, network, wrapper, retry, or orchestration overhead outside provider runtime. - If provider-native prompt/generation/provider-total rates are
n/a, the selected provider did not expose comparable timing data. OpenAI-compatible endpoints may still provide token counts and PatchPatrol wall throughput. - Do not enable raw prompt, completion, diff, API-key, or provider-payload
logging for performance debugging. Use
meta.usage_ledgerandmeta.performance_diagnosticsmetadata instead.
Next action: Compare Generation tok/s, Load time (s), and
Overhead (s) before changing model size, review bounds, timeouts, or runner
placement.
Read artifact metadata like a runbook
Open .ai-review/ai-review.json whenever the stderr line tells you what failed but not what to do next.
| Field | What it answers | Use it when |
|---|---|---|
meta.diff_base | Which base strategy selected the reviewed patch and whether diff-context resolution failed early | Branch/MR output looks like it includes target-branch progress, or stderr mentions MERGE_BASE_NOT_FOUND / GITLAB_DIFF_BASE_REF_NOT_FOUND |
meta.review_coverage | What chunks were attempted, succeeded, retried, fallbacked, omitted, or skipped, with reviewed/skipped byte counts | Scope looked smaller than expected, provider output fallback happened, or provider runtime failed before review output |
meta.limits | Which review limits applied, which chunks or files were omitted, and which limit/error flags fired | Diff extraction failed, scope looked smaller than expected, or EXIT_REASON[FAIL_ON_PARTIAL_COVERAGE] fired |
meta.license | Whether Self-Hosted License evaluation blocked a real-provider review before diff/provider work | The run exited 12 or stderr mentioned LICENSE_ERROR[...] |
meta.provider_runtime | Whether the provider failed in config, capability probe, transport, or provider response stages | You see PROVIDER_ERROR[...], OPENAI_API_ERROR[...], OLLAMA_API_ERROR[...], or OPENAI_CONFIG_ERROR[...] |
meta.trust_gate | Whether the provider call was blocked by allowlist or redaction policy and which TRUST_* code applied | The run exited 11 or stderr mentioned the trust gate |
meta.usage_ledger | Token counts, wall throughput, and provider-native prompt/generation rates when available | Token usage or throughput values look surprising |
meta.performance_diagnostics | Runtime diagnostics, configured bounds, limit-hit evidence, and elapsed-time breakdowns | Performance diagnostics are enabled and you need provider time, wall time, load time, or overhead |
meta.feedback.delivery | Whether GitLab delivery was ready, suppressed, blocked, or failed after artifacts were written | MR note delivery failed, draft/manual suppression is suspected, or artifacts exist without GitLab feedback |
Runbook habit:
- Start with the HTML or Markdown Run Report for the human-readable result.
- Move to
.ai-review/ai-review.jsonfor the matchingmeta.*section. - Take one next action from the lane above instead of changing multiple variables at once.
Provider configuration failures
Use this shortcut when the access/readiness lane points to provider config specifically.
OPENAI_CONFIG_ERROR[...]means PatchPatrol rejected provider settings before review execution.- The most common public fixes are a valid
OPENAI_BASE_URL, the right secret handoff forOPENAI_API_KEY, and removing unsupportedAI_REVIEW_NUM_CTXwhenAI_REVIEW_PROVIDER=openai. - After the fix, rerun
ai-review test --chat --readiness-jsonwith a valid Self-Hosted License before a full artifact-only review.
Provider runtime failures
Use this shortcut when review execution reached the provider but did not finish cleanly.
OPENAI_API_ERROR[...],OLLAMA_API_ERROR[...], andPROVIDER_ERROR[...]belong here.- Read
meta.provider_runtimebefore changing timeouts or models so you know whether the failure came from transport, endpoint contract, or provider response. - If the same failure repeats with
TRANSPORT_RETRY_EXHAUSTED, fix endpoint availability or CI network path before widening the retry budget.
Diff extraction and scope misses
Use this shortcut when the repository/diff lane points to selection or omission problems.
DIFF_CONTEXT_ERROR[...]means PatchPatrol could not resolve the intended review target.DIFF_EXTRACTION_ERROR[...]means PatchPatrol resolved the target but could not extract a usable review scope.meta.limits.chunk_omitted_reasonstells you whether the miss came from large files, chunk caps, or another bounded-review control.
GitLab feedback failures
Use this shortcut when artifacts exist but merge request delivery still failed.
GITLAB_NOTE_ERROR[...]happens after artifact writing, so the first recovery move is always to open the artifacts.meta.feedback.delivery.statusandmeta.feedback.delivery.reason_codestell you whether the failure was a real GitLab transport problem or a suppression state such as draft/manual gating.- If your team is still in
AI_REVIEW_FEEDBACK_MODE=artifact-only, keep using artifacts and do not treat missing MR notes as a broken baseline.
Artifact location confusion checklist
If the developer cannot find results after a run:
- Verify the job publishes
.ai-review/ai-review.html,.ai-review/ai-review.md, and.ai-review/ai-review.jsonas artifacts. - Confirm
AI_REVIEW_OUTPUT_DIRstill resolves to.ai-review. - If artifacts are missing entirely, inspect the runner log around
ARTIFACT_ERROR[...]or exit code6. - If needed, reproduce locally with
ai-review run --output-dir ./tmp-review-debug --mode auto --dry-run.
Return to recovery flow
Use these pages once the immediate failure is understood: