Troubleshooting JUnit XML test result uploads
When a JUnit XML upload to TestNod does not behave the way you expect, work the list below from top to bottom. Each section starts with the symptom and gives the most common cause first.
I get a 404 from the upload endpoint
The response body says "Project not found." This always means the Project-Token header did not match a project. Check, in order:
- Is the secret set in CI? This is the most common cause. Most providers do not raise an error when a referenced secret is missing, and instead substitute an empty string. Print a redacted check in your CI step (
echo "${TESTNOD_PROJECT_TOKEN:0:6}...") to confirm the variable is present. - Is it the right project's token? Each project has its own token. If you recently rotated the token, make sure CI has the new one.
- Is the secret available to this job? GitHub Actions does not expose secrets to forks or to jobs running from forks. GitLab CI masks variables but does not pass them to protected branches unless explicitly configured.
I get a 422 from the upload endpoint
Two distinct 422 bodies come from the upload endpoint. Read the error_message:
"metadata.build_id is required."means you did not sendtest_run[metadata][build_id]. The endpoint will not accept an upload without it, so pick whichever CI variable uniquely identifies the build and pass it through."There was a problem creating this test run."means the token is valid but the request itself was malformed. Check that metadata fields are scalar strings and that tags usetags[][value]=smokerather than a flattags=smoke. The JUnit file is not part of this request. It goes to the presigned URL the upload returns, so a missing or misnamed file does not cause this error.
I get a 422 from the finalize endpoint
Two distinct 422 bodies come from the finalize endpoint:
"build_id is required."means thebuild_idform field on the finalize call was missing. Note that finalize takesbuild_idat the top level, not nested undertest_run[metadata]."No uploads received for this test run."means a run exists for thatbuild_idbut no upload was attached to it. Either the upload step failed silently (check the 4xx/5xx responses there first) or the upload used a differentbuild_idthan finalize did.
My run is stuck in pending
The most common cause is a missing /finalize call. Upload alone does not transition the run, so the finalize call is required to complete it. Check, in order:
- Did the finalize step run? Look at your CI log for the
POST /integrations/test_runs/finalizecall. If it never ran (the previous step exited early, the conditional was wrong, the matrix never converged), call finalize manually with the rightbuild_id. The call is idempotent and safe to retry. - Did finalize use the same
build_idas the uploads? Both calls must agree. If they disagree, finalize gets a 404 and the run stays pending. - Are uploads still pending? Open the run page, where each upload shows its state. If any upload is still in
pendingafter a minute or two, contact [email protected] with thetest_run_idand the TestNod team will look into it.
The upload returns 201 but no run appears
The response includes a test_run_url. Open it directly. If the page loads but the run is in pending, see "My run is stuck in pending" above. If the page loads but the run is in processing for a long time, see "My run is stuck in processing" below. If the page 404s, the response was likely from a different project than the one you have open in the UI, so double-check which project the token belongs to.
My run is stuck in processing
Wait two minutes, then refresh. If it has not moved:
- Check the size of the XML. Files larger than a few hundred MB take longer to parse.
- Check the file is valid XML. Run it through
xmllint --noout report.xmllocally. - Check that the file is actually JUnit XML. Some CI dialects produce a wrapper format (Allure, ReportPortal native) that is not JUnit XML even if the file extension says
.xml. - If none of the above explain it, the run will move to
failedshortly with a stored failure message.
My run finished but a test I expected is missing
The case identity is (classname, name). If either changes between runs, TestNod treats it as a different case. Check the raw XML for the case on a passing run vs. a failing run and compare both attributes character-for-character.
I'm getting parse errors I cannot diagnose
Every upload stores exactly what your CI client sent in an internal submission_params field (request headers, including your project token, are not captured). Email [email protected] with your project's public ID and the run's public ID, and the TestNod team will look at the stored payload.