Skip to content

bub-tapestore-otel exports 0 spans: ValidationError "Input should be ... an instance of TapeEntry" against current bub git HEAD #48

Description

@CorrectRoadH

bub-tapestore-otel exports 0 spans: ValidationError: ... Input should be a dictionary or an instance of TapeEntry when used against current bub git HEAD

Summary

bub-tapestore-otel silently exports zero spans when installed into an
environment that resolves bub to git HEAD (e.g. uv tool install bub --with bub-tapestore-otel, or any fresh resolution that isn't pinned to the workspace
lock). Every tape append throws a pydantic ValidationError, which the
exporter swallows via its try/except … warning("tapestore.otel.export_failed"),
so the only visible symptom is export_failed log lines and no telemetry.

Symptom (exact error)

pydantic_core._pydantic_core.ValidationError: 10 validation errors for StepTrace
entries.0
  Input should be a dictionary or an instance of TapeEntry
  [type=dataclass_type, input_value=TapeEntry(id=1, kind='anc...'), input_type=TapeEntry]
    For further information visit https://errors.pydantic.dev/2.14/v/dataclass_type
entries.1
  Input should be a dictionary or an instance of TapeEntry
  [type=dataclass_type, input_value=TapeEntry(id=2, kind='eve...'), input_type=TapeEntry]
...

Note input_type=TapeEntry — the value is a TapeEntry, yet pydantic rejects
it. Raised from OTelTapeExporter._appendbuild_tape_traceStepTrace(**…),
caught in OTelTapeStore.append and logged as tapestore.otel.export_failed.

Root cause (bisected)

It is a bub version drift, not pydantic and not republic. Controlled A/B,
identical everything except the pinned variable:

bub republic pydantic result
0.3.9.dev6+g10fa0150a (the commit uv.lock pins) 0.5.8 2.13.4 ✅ projects + exports
0.3.9.dev6+g10fa0150a 0.5.8 2.14.0a1 ✅ projects + exports
0.3.10.dev9+ga2b638865 (git HEAD) 0.5.8 2.13.4 export_failed, 10 validation errors
0.3.10.dev9+ga2b638865 (git HEAD) 0.5.8 2.14.0a1 export_failed, 10 validation errors
  • The plugin's own unit tests pass under both pydantic 2.13.4 and 2.14.0a1
    (they build TapeEntry via TapeEntry.system()/.message()/.event()).
  • republic is 0.5.8 in every case, and republic.TapeEntry is republic.tape.entries.TapeEntry → a single class object, not a duplicate-import
    problem.
  • Therefore the trigger is the runtime TapeEntry instances produced by bub
    git HEAD
    (note the new kind='anchor' entries). They fail an isinstance
    check against the republic.TapeEntry captured in the plugin's pydantic schema,
    even though their repr/input_type say TapeEntry.

Why fresh installs hit HEAD

bub-tapestore-otel's pyproject.toml declares dependencies = ["bub", …]
(unpinned) and is consumed from the bub-contrib uv workspace, which sources
bub from git. A consumer doing uv tool install bub>=0.3.0a1 --with git+…/bub-tapestore-otel resolves bub to git HEAD, not the workspace's
locked commit 10fa0150a. (--prerelease allow, needed for bub's 0.3.0a1
spec, additionally pulls pydantic 2.14.0a1, but that is harmless per the table
above.)

Minimal repro

# FAILS (bub HEAD)
uv run --no-project --prerelease allow \
  --with "bub>=0.3.0a1" \
  --with "git+https://github.com/bubbuild/bub-contrib.git#subdirectory=packages/bub-tapestore-otel" \
  bub --workspace /tmp/ws run "read data.txt" --session-id x
# → tapestore.otel.export_failed ... ValidationError: ... instance of TapeEntry

# WORKS (pin bub to the tested commit)
uv run --no-project --prerelease allow \
  --with "bub @ git+https://github.com/bubbuild/bub.git@10fa0150a1168abfe67449380f714ac9456b3b7b" \
  --with "git+https://github.com/bubbuild/bub-contrib.git#subdirectory=packages/bub-tapestore-otel" \
  bub --workspace /tmp/ws run "read data.txt" --session-id x
# → projects + attempts OTLP export, no export_failed

Suggested fixes (upstream)

  1. Make the projection robust to TapeEntry identity/shapeentries (and
    the TapeEntry-typed fields) are internal-only; they don't need pydantic
    validation. Type them as SkipValidation[list[TapeEntry]] (pydantic) or
    list[Any], so the plugin survives any bub/republic change to how
    TapeEntry is constructed. This is the real durability fix.
  2. Constrain bub in pyproject.toml to the tested range, and/or publish
    bub-tapestore-otel to PyPI so consumers don't resolve bub to git HEAD.
  3. If bub HEAD intentionally changed TapeEntry construction/identity, treat
    that as a breaking change for plugins that receive TapeEntry instances.

Related

Same failure mode (a contrib plugin breaking against current bub main) as
#43 "bub-tapestore-sqlite breaks against latest bub main after _build_llm
removal"
— different plugin/symptom, same root pattern of unpinned bub drift.

Consumer workaround (until fixed)

Pin bub to the plugin's tested commit when installing:
bub @ git+https://github.com/bubbuild/bub.git@10fa0150a1168abfe67449380f714ac9456b3b7b

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions