Examples below assume the generated repository is named
test_optimization_data. If you used a different name, replace labels and
bazel sync --only=<repo_name> accordingly.
If Bazel reports that sync requires WORKSPACE support, add
--enable_workspace to sync commands in this document.
| Symptom | First checks | Likely section |
|---|---|---|
| No files fetched or stale data | DD_API_KEY forwarded with --repo_env; use FETCH_SALT only for an explicit force-refresh sync |
Repository rule not fetching data |
| Uploader says no payload files | tracer file-mode contract + payload files under bazel-testlogs/*/test.outputs/ |
Uploader not finding payloads |
| Doctor reports msgpack payloads | tracer is not in Bazel JSON file mode | Doctor failures |
| Doctor reports missing Git or Bazel metadata | sync metadata context or sidecar metadata is absent | Doctor failures |
| Uploaded tests miss Git or Bazel tags | run uploader dry-run enrichment validation | Uploader enrichment dry-run |
| Upload network errors | credential mode (agentless vs EVP), intake reachability | Tests not uploading (network errors) |
| CI failure requires log archaeology | archive the support bundle from the failing run | Collect diagnostic reports |
| Module selection misses | bazel query for module_* targets and importpath/module label expectations |
Per-module files not found |
| Go build fails with a tracer version mismatch | dd_trace_go_version, dd_trace_go_versions, --dd-trace-go-version, local go.mod pins |
Go tracer version drift |
| Bazel resolves an older tracer or Orchestrion module in WORKSPACE mode | checked-in go_repository(...) pins |
WORKSPACE go_repository drift |
| WORKSPACE archive pins fail after a PR was squash-merged | generated pins commit reachability and archive SHA | Published Go pins |
| Private/internal WORKSPACE fetch returns 404 | SSH git or authenticated archive access | Private repository fetch |
| Bazel downloads unrelated toolchains or analyzes unrelated packages | cold monorepo state or root-package doctor/uploader placement | Monorepo analysis cost |
| Windows path/policy failures | PowerShell policy + path separators | Windows-specific issues |
When a failure is hard to classify from logs alone, archive a support bundle from the same run. For the simplest customer ask after tests have already run, use the doctor directly:
bazel run //:dd_test_optimization_doctor -- \
--support-bundle .topt/reports/dd-test-optimization-support.zipFor BEP/BwoB CI investigations, include the same BEP and artifact flags used by the failing job:
bazel run --config=test-optimization //:dd_test_optimization_doctor -- \
--bep-json="$bep_json" \
--freshness-source=bep \
--freshness-mode=required \
--artifact-source=bep \
--artifact-staging-dir="$artifact_staging_dir" \
--support-bundle=.topt/reports/dd-test-optimization-support.zipThe doctor bundle is doctor-only: it captures expected targets, BEP freshness, artifact staging, discovered payload directories, payload counts, status, exit code, effective doctor flags, runtime metadata, and selected BEP summaries. It does not prove uploader dry-run, enrichment, API-key, or upload behavior.
Use this escalation ladder:
| Situation | Ask for | Why |
|---|---|---|
| First customer response after tests already ran | Doctor --support-bundle=<path> |
Smallest command; built into the doctor target; no helper scripts required |
| CI failure where upload, enrichment, or dry-run behavior matters | Wrapper --report-dir=<path> --support-bundle=<path> |
Includes doctor, uploader dry-run, optional upload report, BEP summaries, and effective wrapper flags |
| Repository cannot run the wrapper or doctor bundle | Raw --report-json files plus manual create_support_bundle.py output |
Fallback only; raw reports may include internal paths until the redacted zip is created |
Ask the customer to attach the zip, not screenshots of terminal output. If the
bundle is not available, ask for the individual JSON reports and the exact
bazel test, doctor, and uploader commands.
Use the wrapper flow when the CI job should also validate uploader dry-run or perform the real upload:
# Vendor the full tools/test_optimization/ helper directory, or set
# DD_TEST_OPTIMIZATION_SUPPORT_BUNDLE_COLLECTOR to create_support_bundle.py.
tools/test_optimization/run_test_optimization_ci.sh \
--report-dir .topt/reports \
--support-bundle .topt/reports/dd-test-optimization-support.zip \
//...# Vendor the full tools/test_optimization/ helper directory, or set
# DD_TEST_OPTIMIZATION_SUPPORT_BUNDLE_COLLECTOR to create_support_bundle.py.
.\tools\test_optimization\run_test_optimization_ci.ps1 `
-ReportDir .topt\reports `
-SupportBundle .topt\reports\dd-test-optimization-support.zip `
//...The wrapper writes doctor-report.json and uploader-dry-run-report.json
under the report directory. If the same wrapper run includes the real upload,
it also writes uploader-upload-report.json, preserving the dry-run report.
When --support-bundle or -SupportBundle is configured, it also writes
dd-test-optimization-support.zip.
Each report has a result.reason_code, human-readable result.reason, and
result.next_steps so support can distinguish cached tests, remote-only BEP
artifacts, missing payloads, enrichment failures, dry-run no-upload, and real
upload failures without reading the full log.
When opening a support ticket, attach
.topt/reports/dd-test-optimization-support.zip. The bundle is redacted by
default and does not contain raw payload files, raw CI logs, raw environment
variables, or the raw BEP. Depending on whether the doctor or wrapper created
it, it contains bounded doctor-only or doctor/uploader reports, selected BEP
summaries, command/flags.json, environment/runtime.json, summary.md, and
diagnostics.json.
Triage the bundle in this order:
- Open
summary.mdfor the human-readable failure classification. - Check
diagnostics.jsonforsummary.status,summary.primary_reason_code, payload counts, and included report paths. - Check
reports/doctor-report.jsonfor expected targets, seen targets, fresh/cached/remote-only BEP outputs, artifact staging, and doctor errors. - If the wrapper created the bundle, check
reports/uploader-dry-run-report.jsonfor payload discovery, enrichment validation, skipped payloads, and no-upload reasons. - If upload was enabled, check
reports/uploader-upload-report.jsonfor the real upload attempt count, HTTP status, and terminal upload failure. - Check
command/flags.jsonto confirm the test run used a unique BEP file and that doctor/uploader ran with the matching--bep-json,--freshness-source=bep,--freshness-mode=required,--artifact-source=bep, and artifact staging flags.
Manual doctor/uploader flow:
bazel run --config=test-optimization //:dd_test_optimization_doctor -- \
--bep-json="$bep_json" \
--freshness-source=bep \
--freshness-mode=required \
--artifact-source=bep \
--artifact-staging-dir="$artifact_staging_dir" \
--report-json=.topt/doctor-report.json
bazel run --config=test-optimization //:dd_upload_payloads -- \
--bep-json="$bep_json" \
--freshness-source=bep \
--freshness-mode=required \
--artifact-source=bep \
--artifact-staging-dir="$artifact_staging_dir" \
--dry-run \
--validate-enrichment \
--report-json=.topt/uploader-report.jsonArchive the JSON files as CI artifacts when a repository cannot use the wrapper support bundle. If the repository has the helper script available, create the same redacted zip from existing reports:
python3 tools/test_optimization/create_support_bundle.py \
--report-dir .topt/reports \
--report-json .topt/reports/doctor-report.json \
--report-json .topt/reports/uploader-dry-run-report.json \
--output .topt/reports/dd-test-optimization-support.zip \
--workspace-root "$PWD" \
--output-base "$(bazel info output_base)"Use repeatable --bep-json=<path> flags with that command when the reports came
from BEP/BwoB runs. If only a Markdown summary is possible, render a short
fallback summary:
python3 tools/test_optimization/render_report_summary.py \
.topt/reports/doctor-report.json \
.topt/reports/uploader-dry-run-report.json \
--output .topt/reports/upload-diagnostics.mdBefore sharing raw reports outside the trusted project boundary, review them for repository paths, target names, and service metadata. They must not contain API keys, but they can contain internal labels and filesystem paths. Prefer a support bundle for external escalation because it redacts and bounds those values automatically.
Symptom: Build succeeds but test optimization files are empty or stale.
Solutions:
-
Verify DD_API_KEY is set:
env | grep '^DD_API_KEY=' grep -n 'common --repo_env=DD_API_KEY' .bazelrc
$env:DD_API_KEY Select-String -Path .bazelrc -Pattern "common --repo_env=DD_API_KEY"
If missing, set
DD_API_KEYin your shell/CI secret store and add to.bazelrc:common --repo_env=DD_API_KEY -
Force refetch only when intentional with a cache-busting salt:
bazel sync --only=<repo_name> --repo_env=FETCH_SALT="$(date +%s)"
bazel sync --only=<repo_name> --repo_env=FETCH_SALT="$(Get-Date -UFormat %s)"
Do not put
FETCH_SALTin.bazelrc,bazel test, doctor, or uploader commands. It deliberately breaks the repository-rule cache key and should be used only when you want fresh backend metadata. -
Check repository cache to see if the rule ran:
# Find the external repository directory bazel info output_base # Repository contents at: $(bazel info output_base)/external/<repo_name> ls -la $(bazel info output_base)/external/<repo_name>/.testoptimization/
# Find the external repository directory $outputBase = bazel info output_base # Repository contents at: "$outputBase/external/<repo_name>" Get-ChildItem -Force "$outputBase/external/<repo_name>/.testoptimization/"
-
Enable debug logging in your
MODULE.bazel:test_optimization_sync.test_optimization_sync( name = "test_optimization_data", debug = True, # Verbose logging )
Symptom: A consumer cannot fetch the published Go/Orchestrion archive, or a pin that worked during review stops working after a squash merge.
Cause: The consumer was given a feature-branch commit, a stale archive hash, or a tuple that was not generated from the real GitHub codeload archive.
Solutions:
-
Regenerate pins from
origin/mainin this repository:./bazelw run //tools/dev:print_go_onboarding_pins -- \ --commit "$(git rev-parse origin/main)" \ --rules-go-upstream v0_60_0 \ --variant base \ --verify-main-reachable -
If you are in a consumer repo, use the bootstrap helper with the squash-merged commit:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --print-published-pins \ --rto-commit <published-origin-main-sha> \ --rules-go-upstream v0_60_0 \ --rules-go-variant base
-
Keep the generated tuple together. If
RTO_COMMIT,RTO_ARCHIVE_URL,RTO_ARCHIVE_SHA256, andRTO_ARCHIVE_PREFIXcome from different commits, archive mode will fail or fetch the wrong source. The current published tuple is tracked indocs/Installation_Reference.md. -
Authenticate private archive downloads. The helper uses
GITHUB_TOKEN,GH_TOKEN, orgh auth tokenwhen available. If codeload returns404for a commit you can see in GitHub, refresh local GitHub CLI authentication or set one of those token variables before regenerating pins. -
Use the generated summary for handoff when a monorepo needs a permanent local guide:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --write-onboarding-summary=TEST_OPTIMIZATION_GUIDE.md \ --rto-commit <published-origin-main-sha> \ --rules-go-upstream v0_60_0 \ --rules-go-variant base
Symptom: A WORKSPACE consumer gets 404 when fetching the rules archive or
companion archive from a private/internal repository.
Cause: Anonymous codeload archive URLs can return 404 for private
repositories even when the commit exists and the user can see it in GitHub.
Solutions:
-
Prefer SSH git fetch for internal Datadog consumers:
git_repository( name = "datadog-rules-test-optimization", commit = "<commit-sha>", remote = "ssh://git@github.com/DataDog/rules_test_optimization.git", )
-
Use archive fetch only when Bazel has compatible authentication, for example
.netrcor another repository-approved token mechanism. -
Do not commit local archive paths or local checkouts as a workaround for CI. Use local paths only for temporary local validation.
Symptom: Tests already produced payload files, but running doctor or uploader appears slow because Bazel downloads unrelated toolchains or analyzes unrelated packages.
Cause: Some cost is normal cold-cache monorepo behavior. Another common
cause is placing doctor/uploader in the workspace root package, which can load
global root BUILD.bazel wiring unrelated to Test Optimization.
Solutions:
-
Move the logical doctor/uploader pair to a lightweight package:
# tools/test_optimization/BUILD.bazel load("@datadog-rules-test-optimization//tools/core:test_optimization_targets.bzl", "dd_test_optimization_targets") dd_test_optimization_targets( name = "test_optimization", sync_repo_name = "test_optimization_data", expected_targets = ["//app:service_py_test"], )
-
Run package-local labels:
bep_json="${bep_json:-/path/from/matching/bazel-test.bep.json}" bazel run --config=test-optimization //tools/test_optimization:dd_test_optimization_doctor -- \ --bep-json="$bep_json" \ --freshness-source=bep \ --freshness-mode=required bazel run --config=test-optimization //tools/test_optimization:dd_upload_payloads -- \ --bep-json="$bep_json" \ --freshness-source=bep \ --freshness-mode=required \ --dry-run \ --validate-enrichment
-
If Bazel repeatedly refetches Test Optimization metadata, check whether a script or
.bazelrcis settingFETCH_SALTby default.
Symptom: Uploader runs but says "no payload files found".
Solutions:
-
Check if tests wrote payloads:
find bazel-testlogs -name "test.outputs" -type d ls bazel-testlogs/*/test.outputs/payloads/tests/
Get-ChildItem -Recurse -Directory -Path bazel-testlogs -Filter "test.outputs" Get-ChildItem -Recurse -Path bazel-testlogs -File -Filter "*.json" | Where-Object { $_.FullName -match "test\.outputs[\\/]+payloads[\\/]+tests" }
-
Verify tracer support: Ensure your tracer/runtime supports file mode via
DD_TEST_OPTIMIZATION_PAYLOADS_IN_FILESand manifest discovery viaDD_TEST_OPTIMIZATION_MANIFEST_FILE. -
Check DD_TEST_OPTIMIZATION_PAYLOADS_IN_FILES: The macro should set this to
"true". Verify your test environment:bazel test //your:test --test_output=all 2>&1 | grep DD_TEST_OPTIMIZATION
bazel test //your:test --test_output=all *>&1 | Select-String "DD_TEST_OPTIMIZATION"
PowerShell uses
*>&1(not Bash2>&1) to merge stderr/stdout. -
For RBE/remote-cache users: Put
--remote_download_minimal,--remote_download_regex=.*test[.]outputs.*, and--zip_undeclared_test_outputsin the active test.bazelrcconfig. Run tests with a fresh--build_event_json_file=..., then run doctor/uploader with repeatable--bep-json=<path>,--freshness-source=bep,--freshness-mode=required,--artifact-source=bep, and--artifact-staging-dir=<temp-dir>so cached outputs are skipped and localoutputs.zipcarriers are extracted through BEP staging.
Symptom: bazel run --config=test-optimization //:dd_test_optimization_doctor
fails before upload.
Solutions:
-
Msgpack payloads: Go/Orchestrion Bazel mode must write JSON payloads to
TEST_UNDECLARED_OUTPUTS_DIR. If the doctor finds.msgpackor.msgpack.gz, verify that the target uses the Go test optimization macro or wrapper and that the configureddd-trace-goversion includes Bazel JSON file-mode support. -
No JSON payloads: Confirm the test target actually ran with Test Optimization enabled and inspect:
find bazel-testlogs -path '*/test.outputs/payloads/*/*.json' -type fFor Python WORKSPACE consumers, confirm the target loads
dd_topt_py_testfrom@datadog-rules-test-optimization-python//:topt_py_test.bzland that the companion repository was declared withdatadog_python_test_optimization_workspace_repositories(...). For Pythonrunner_mode = "consumer_runner", also verify that the consumer-owned wrapper really executes pytest, propagates theenvpassed bydd_topt_py_test, and keepsPYTEST_ADDOPTS=--ddtraceunless it intentionally sets--no-ddtrace. Ifenvis aselect(...), the macro cannot addPYTEST_ADDOPTSinside each branch; include--ddtracein every relevant selected environment. The target must also depend onddtraceandpytest. -
Missing Git metadata: The sync metadata fetch must see repository URL, commit SHA, and branch or tag. Put
DD_GIT_*values in.bazelrcascommon:test-optimization --repo_env=DD_GIT_<NAME>, not--test_env. The doctor scans versioned.bazelrcfiles for--test_env=DD_GIT_*, but it cannot detect a bad--test_env=DD_GIT_*flag typed directly on the CLI. -
Missing Bazel metadata: The target should emit
bazel_target_metadata.jsonnext to payload files. Use the companion macro or generated wrapper instead of invoking the raw language test rule directly.For Python consumer-owned wrappers, the wrapper must expose an executable target with
RunEnvironmentInfopreserved from the rawpy_test; otherwise the public metadata wrapper can run but the actual pytest process may miss the requiredDD_TEST_OPTIMIZATION_*environment.In WORKSPACE mode, also verify that the Python helper maps the companion's internal
@rules_pythondependency to the consumer repository name:datadog_python_test_optimization_workspace_repositories( rto_commit = "<commit-sha>", rules_python_repo_name = "rules_python", # or the consumer's custom name )
-
full_bundle_no_match: The Go macro could not map the test target to a per-module bundle. Preferembed = [":lib"]so the macro can read the same importpath thatrules_gouses, or setmodule_label_overrideonly when the module label is intentionally known.moduleandmodule_overrideare valid successful selections;full_bundle_disabledis valid when backend module data is disabled. -
Expected target output missing: Run the exact target listed in
expected_targetsbefore the doctor. With remote execution or remote cache, use the recommended test.bazelrcconfig:--remote_download_minimal --remote_download_regex=.*test[.]outputs.* --zip_undeclared_test_outputs. ExportDD_TEST_OPTIMIZATION_ARTIFACT_SOURCE=bepfor doctor/uploader; if BEP references remote-only artifacts, enable BEP artifact resolution with--artifact-source=bep --remote-artifacts=download.
Symptom: The doctor or uploader is configured with BEP freshness, but
loose test.outputs/payloads/... files are not present under bazel-testlogs/
because the Bazel invocation used remote execution/cache, zipped undeclared
outputs, or left the selected BEP artifacts remote-only.
Solutions:
-
Prefer selective local materialization plus zipped undeclared outputs when Bazel can download the needed outputs:
test:test-optimization --remote_download_minimal test:test-optimization --remote_download_regex=.*test[.]outputs.* test:test-optimization --zip_undeclared_test_outputs -
Pass the same BEP file to doctor/uploader and enable local BEP artifact staging. The checked-in CI wrapper does this with repeatable CLI flags:
tools/test_optimization/run_test_optimization_ci.sh //...
Manual invocations should use the same temporary BEP and staging paths:
bep_json="${bep_json:-/path/from/matching/bazel-test.bep.json}" artifact_staging_dir="${artifact_staging_dir:-$(mktemp -d "${TMPDIR:-/tmp}/dd-topt-artifacts.XXXXXX")}" bazel run --config=test-optimization //:dd_test_optimization_doctor -- \ --bep-json="$bep_json" \ --freshness-source=bep \ --freshness-mode=required \ --artifact-source=bep \ --artifact-staging-dir="$artifact_staging_dir" bazel run --config=test-optimization //:dd_upload_payloads -- \ --bep-json="$bep_json" \ --freshness-source=bep \ --freshness-mode=required \ --artifact-source=bep \ --artifact-staging-dir="$artifact_staging_dir" \ --dry-run \ --validate-enrichment
--remote-artifactsis not required for localoutputs.zipfiles. -
If CI cannot download
test.outputsoroutputs.ziplocally, pass the same BEP file to doctor and uploader and enable remote staging. HTTP/HTTPSoutputs.zipcarriers can be staged without a downloader:artifact_staging_dir="${artifact_staging_dir:-$(mktemp -d "${TMPDIR:-/tmp}/dd-topt-artifacts.XXXXXX")}" bazel run --config=test-optimization //:dd_test_optimization_doctor -- \ --bep-json="$bep_json" \ --freshness-source=bep \ --freshness-mode=required \ --artifact-source=bep \ --remote-artifacts=download \ --artifact-staging-dir="$artifact_staging_dir" bazel run --config=test-optimization //:dd_upload_payloads -- \ --bep-json="$bep_json" \ --freshness-source=bep \ --freshness-mode=required \ --artifact-source=bep \ --remote-artifacts=download \ --artifact-staging-dir="$artifact_staging_dir" \ --dry-run \ --validate-enrichment
-
For bytestream/CAS/custom provider BEP artifact references, or HTTP/HTTPS endpoints that require custom auth, configure one downloader executable:
export DD_TEST_OPTIMIZATION_BEP_ARTIFACT_DOWNLOADER=/path/to/download-outputs-zipThe executable receives
--uri,--name, and--outputarguments and must write anoutputs.ziparchive to--outputbefore--bep-artifact-downloader-timeout-secexpires. If your provider needs an interpreter, fixed arguments, custom auth headers, mTLS, signed header refresh, or a CAS client, wrap it in a script and configure the wrapper path. The public rule does not ship credentials or a Datadog-internal CAS client.
Native HTTP/HTTPS staging is unauthenticated. It retries transient network failures, truncated responses, HTTP 408, HTTP 429, and HTTP 5xx responses three times with exponential backoff. It does not add custom headers, read credentials, use cookies, or consult netrc. Query strings, fragments, usernames, and passwords are redacted from doctor/uploader warnings and JSON reports.
Common messages:
--artifact-source=bep requires --bep-json or DD_TEST_OPTIMIZATION_BEP_JSON: pass the BEP JSON produced by the matchingbazel testinvocation.BEP artifact is remote-only and no downloader is configured: either use--remote_download_minimal --remote_download_regex=.*test[.]outputs.* --zip_undeclared_test_outputswith--artifact-source=bep, enable--remote-artifacts=downloadfor HTTP/HTTPSoutputs.zipcarriers, configureDD_TEST_OPTIMIZATION_BEP_ARTIFACT_DOWNLOADERfor bytestream/CAS/custom-auth providers, or use--remote-artifacts=disabledoutside required freshness mode.BEP HTTP artifact download failed ... HTTP Error 401or403: the endpoint likely requires auth. Use a downloader wrapper that injects the required credentials or headers.BEP HTTP artifact download failed ... HTTP Error 404: confirm the BEP URI still exists for the current job and has not expired.BEP HTTP artifact download failed ... timed out: native HTTP retries transient failures three times; raise--bep-artifact-downloader-timeout-seconly when each request attempt needs more time.invalid BEP outputs.zip: confirm the URL serves Bazel'soutputs.zip, not HTML, JSON, or an error page.BEP HTTP artifact is too large: inspect undeclared test outputs for unexpected files; the compressed download and extracted tree both have size guards.BEP artifact is not available locally: the BEP points at a local path orfile://URI that does not exist in this workspace. Check that the BEP file matches the current test invocation and machine.remote-artifacts=required: strict artifact mode is all-or-nothing. Missing, ambiguous, unsafe, or undownloadable selected BEP artifacts fail the command instead of falling back to local payloads.unsafe path in BEP outputs.zip: the archive contains an absolute path or parent traversal entry. Regenerate the archive or fix the downloader.BEP outputs.zip has too many entries: the archive exceeds the staging entry limit and is rejected before extraction.BEP outputs.zip is too large after decompression: the archive exceeds the staging decompressed-size limit.BEP artifact staging requires PYTHON, python3, or python: the uploader needs Python only when artifact staging is enabled. SetDD_TEST_OPTIMIZATION_PYTHONorPYTHONwhen Python is not discoverable aspython3orpythononPATH.
Symptom: Payload files exist, but Datadog UI or JSON inspection suggests the uploaded test is missing Git or Bazel tags.
Explanation: Raw test payloads on disk are not the final upload body. The
uploader adds context.json and bazel_target_metadata.json tags immediately
before upload.
Solution: Run the uploader dry-run after tests and doctor:
bep_json="${bep_json:-/path/from/matching/bazel-test.bep.json}"
bazel run --config=test-optimization //:dd_upload_payloads -- \
--bep-json="$bep_json" \
--freshness-source=bep \
--freshness-mode=required \
--dry-run \
--validate-enrichment$env:BEP_JSON = "C:\path\from\matching\bazel-test.bep.json"
bazel run --config=test-optimization //:dd_upload_payloads -- `
--bep-json="$env:BEP_JSON" `
--freshness-source=bep `
--freshness-mode=required `
--dry-run `
--validate-enrichmentDry-run mode does not upload, does not delete payload files, and does not need
DD_API_KEY for agentless mode. By default it requires
git.repository_url, git.commit.sha, bazel.target, and bazel.package in
the enriched test payload. Add
--expected-enriched-tag=bazel.go.payload_selection when a Go rollout must
prove per-module selection. If this fails:
- Ensure the uploader target has the right
data = ["@...//:test_optimization_context"]. - Ensure
bazel_target_metadata.jsonexists beside the payloads. - Ensure
jqis available on Linux/macOS when using--validate-enrichment. - Use
--expected-enriched-tag=<tag>for repository-specific required tags.
Symptom: Uploader cannot find the bazel-testlogs directory.
Solution: Set TESTLOGS_DIR explicitly using the same Bazel flags:
# Bash - use array for multiple flags
BAZEL_FLAGS=("--output_base=/custom/base")
bep_json="${bep_json:-/path/from/matching/bazel-test.bep.json}"
TESTLOGS_DIR=$(bazel "${BAZEL_FLAGS[@]}" info bazel-testlogs) bazel "${BAZEL_FLAGS[@]}" run //:dd_upload_payloads -- \
--bep-json="$bep_json" \
--freshness-source=bep \
--freshness-mode=required# PowerShell
$BazelFlags = @("--output_base=/custom/base")
$env:BEP_JSON = "C:\path\from\matching\bazel-test.bep.json"
$env:TESTLOGS_DIR = (bazel @BazelFlags info bazel-testlogs)
bazel @BazelFlags run //:dd_upload_payloads -- `
--bep-json="$env:BEP_JSON" `
--freshness-source=bep `
--freshness-mode=requiredSymptom: Uploader fails with network errors.
Solutions:
-
Verify credentials:
- Agentless mode requires:
DD_API_KEY,DD_SITE - EVP proxy mode requires:
DD_TEST_OPTIMIZATION_AGENT_URL
- Agentless mode requires:
-
Check firewall/proxy allows HTTPS to:
https://citestcycle-intake.datadoghq.comhttps://citestcov-intake.datadoghq.com- (or equivalent for your
DD_SITE)
-
Enable debug logging:
dd_payload_uploader( name = "dd_upload_payloads", debug = True, ... )
Symptom: dd_topt_go_test fails with "module_X not found" or falls back to
full bundle.
Solutions:
-
List available modules:
bazel query 'kind(".*", @<repo_name>//...)' | grep module_
bazel query 'kind(".*", @<repo_name>//...)' | Select-String "module_"
-
Override module label explicitly (workaround):
dd_topt_go_test( name = "my_test", module_label_override = "my_expected_module", # Matches :module_my_expected_module orchestrion_mode = "test_optimization", ... )
Symptom: Go bootstrap or Bazel build fails with a message showing a
configured tracer version map and a different resolved local dd-trace-go
module version.
Solutions:
-
If you use guided bootstrap, rerun it with the query you want:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --dd-trace-go-version <tag-or-sha>
Bootstrap accepts tags, pseudo-versions, branches, and commit SHAs. It rewrites the workspace to the exact resolved versions that Go actually uses. By default it uses targeted module sync, not
go mod tidy. -
If targeted sync reports readonly module errors, rerun bootstrap with explicit targeted sync and the same Go SDK your repository expects:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --go-mod-sync=targeted \ --go-binary /path/to/go
If the workspace uses checked-in
go_repositorydeclarations, refresh those declarations aftergo.modorgo.sumchanges so Bazel and the Go module graph agree. -
If you wire Orchestrion manually, make sure both places match:
orchestrion.from_source(..., dd_trace_go_version = "<version>")- or
orchestrion.from_source(..., dd_trace_go_versions = {...}) - the effective local module graph resolved from
go.modandgo.sum
-
If you omitted the version entirely, remember the default is
v2.9.0.
The build fails on purpose here. It is preventing Bazel from injecting one set of tracer versions while the local Go module still resolves another.
orchestrion.tool.go still matters, but as required import/config wiring for
Orchestrion, not as the source of truth for tracer versions.
Symptom: go.mod and go.sum look correct, but Bazel still resolves an
older github.com/DataDog/orchestrion or dd-trace-go module from a checked-in
repositories.bzl file.
Cause: WORKSPACE repositories often keep generated go_repository(...)
declarations separate from go.mod. Updating the Go module graph is not enough
if the generated repository file still pins old versions.
Solutions:
-
Run bootstrap diagnostics after targeted sync:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --workspace-mode \ --write-orchestrion-files \ --go-mod-sync=targeted \ --check-go-repositories \ --go-repositories-file repositories.bzl
The checker compares only the modules bootstrap owns:
github.com/DataDog/orchestrion,github.com/DataDog/dd-trace-go/v2,github.com/DataDog/dd-trace-go/contrib/net/http/v2, andgithub.com/DataDog/dd-trace-go/contrib/log/slog/v2. -
Let your repo-owned refresh command repair the file:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --workspace-mode \ --write-orchestrion-files \ --go-mod-sync=targeted \ --check-go-repositories \ --go-repositories-file repositories.bzl \ --go-repositories-refresh-command './tools/update-go-repositories.sh'Bootstrap runs the refresh command only after targeted sync succeeds, then validates
repositories.bzlagain. It never editsrepositories.bzldirectly. -
Print expected versions for manual updates:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --workspace-mode \ --go-mod-sync=off \ --check-go-repositories \ --print-go-repository-updates
This is useful when the repository has a custom Gazelle/update-repos flow that must be run manually or in a separate review step.
Symptom: onboarding works in small fixtures but large WORKSPACE repos need a long command sequence, local controls, disk checks, and an explicit upload step.
Solutions:
-
Generate a local validation script instead of hand-copying commands:
bazel run @datadog-rules-test-optimization-go//:dd_topt_go_bootstrap -- \ --workspace-mode \ --write-validation-script \ --bazel-command bzl \ --bazel-config test-optimization \ --sync-repo-name test_optimization_data \ --control-target //pkg/plain:go_default_test \ --expected-target //pkg:go_default_test \ --large-monorepo \ --shutdown-bazel-on-exit
-
Run without upload first:
./tools/test_optimization/validate_go_pilot.sh --no-upload
-
Upload only after tests and doctor pass:
DD_API_KEY="$DD_API_KEY" DD_SITE="$DD_SITE" \ ./tools/test_optimization/validate_go_pilot.sh --upload
The generated script never deletes caches. In --large-monorepo mode it warns
when free disk drops below --min-free-disk-gb, runs phases serially, and can
shut down Bazel on exit. It still depends on the normal Bazel config for
--remote_download_minimal --remote_download_regex=.*test[.]outputs.* and
--zip_undeclared_test_outputs, plus BEP runtime configuration through
DD_TEST_OPTIMIZATION_* or equivalent CLI flags; no rule can force that
client-side Bazel behavior.
Symptom: PowerShell errors or path issues.
Solutions:
-
Verify PowerShell execution policy:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
-
Check paths use forward slashes in Starlark/Bazel contexts (backslashes are auto-converted).
-
Use native PowerShell harness on Windows:
.\tools\tests\integration\run_mock_server_tests.ps1- Git Bash is not required for Windows integration runs.
- Prefer explicit env assignment over shell interpolation in troubleshooting
commands:
- Unix:
DD_API_KEY="$DD_API_KEY" DD_SITE="$DD_SITE" bazel run --config=test-optimization //:dd_upload_payloads - PowerShell: set
$env:DD_API_KEYand$env:DD_SITEfirst, then runbazel run --config=test-optimization //:dd_upload_payloads
- Unix:
- Quote paths containing spaces and avoid
eval-style wrappers. - For refetch debugging, use:
bazel sync --only=<repo_name> --repo_env=FETCH_SALT=<timestamp>- if required by workspace mode:
bazel sync --enable_workspace --only=<repo_name> --repo_env=FETCH_SALT=<timestamp>
If issues persist:
-
Enable debug mode and capture full output:
bazel sync --only=<repo_name> --repo_env=FETCH_SALT=<timestamp> 2>&1 | tee debug.log
bazel sync --only=<repo_name> --repo_env=FETCH_SALT=<timestamp> *>&1 | Tee-Object -FilePath debug.log
-
Collect diagnostic info:
- Bazel version:
bazel version - OS:
uname -a(Linux/macOS) orsysteminfo(Windows) - Repository rule outputs (as shown above)
- Doctor and uploader JSON reports, if the failing run produced them
- Sanitized logs (remove API keys before sharing)
- Bazel version:
-
File an issue:
- open an issue in the repository issue tracker with sanitized logs