Skip to content

Troubleshooting

Documentation home · Configuration · Limitations

Start with the smallest failing operation. A successful import is not proof of a working HBase connection; a successful connection is not proof that every API option works.

1. Installation versus connection problems

Run these in the environment that runs your application:

python -m pip show hbase-driver
python -c "import hbasedriver; print(hbasedriver.__file__)"
python --version

If the import fails, check that pip and Python refer to the same environment. A venv is optional, not required. For a managed system Python that rejects installation, use an existing application environment or create a venv; do not disable system protections as a first fix.

If source edits do not take effect, confirm an editable installation with python -m pip install -e . from the checkout. Check the imported file path before debugging a different installed release.

2. ZooKeeper works but data operations fail

The client first contacts ZooKeeper, then uses the advertised Master/RegionServer hostnames. All of those addresses must resolve and be reachable from the Python process. Exposing only ZooKeeper or mapping ports on localhost is insufficient.

For this repository, run from its root:

docker compose ps
docker compose exec -T dev getent hosts hbase-master hbase-rs1 hbase-rs2 hbase-rs3
docker compose logs --tail=100 hbase-master hbase-rs1 hbase-rs2 hbase-rs3

Use HBASE_ZK=hbase-zk:2181 inside the dev service (hbase_dev container). Container localhost refers to that container, not the Mac/host. Tests belong inside this project's dev container, not an unrelated project's Python container.

The current discovery path assumes /hbase and simple authentication. Do not treat custom znode paths, Kerberos/SASL/TLS, or comma-separated quorum addresses as proof of fully supported secure/HA discovery.

3. Cluster starting or table unavailable

Container health does not always mean every table Region is assigned. Check Master logs/UI, RegionServer registration, table enabled state, and family names. Reuse an already-running development cluster with:

./scripts/run_tests_3node.sh --no-start test/test_batch_operations.py

The runner waits for cluster readiness. Without --no-start, it tears down/restarts the project containers. Do not use its --down option when data must be retained: it removes volumes.

admin.table_exists() currently returns False for any caught exception, including a failed metadata read. Do not respond to False by deleting/recreating an important table. Inspect the original connectivity issue using a read such as describe_table() or cluster logs.

4. Common example/API mismatches

Symptom Likely cause Correct action
Cannot import ColumnFamilyDescriptor That public class does not exist Use ColumnFamilyDescriptorBuilder(...).build()
Cannot import FilterListOperator Operator is a nested enum Use FilterList.Operator.MUST_PASS_ALL
Missing argument for TableName.value_of One combined table name was supplied Pass namespace and qualifier separately
Unexpected include_start_row keyword Scan constructor takes only start_row/end_row Call with_start_row(key, inclusive=False)
NoneType after a BatchGet chain add_column returns None Construct, add columns, then call batch_get
Iterating batch results produces bytes Results are keyed by row key Use results.items() or results[key]
BufferedMutatorParams rejects keywords Constructor takes no parameters Instantiate, then assign attributes
SingleColumnValueFilter setter missing Flags are constructor options Pass filter_if_missing/latest_version_only there

5. Reads return incorrect or unexpected data

  • Missing row: table.get() returns None. Missing cell: row.get() returns None. Empty cell: b"". Avoid truthiness checks when empty values are valid.
  • Text ordering: UTF-8/ASCII numbers sort lexicographically, not numerically. Use an intentional fixed-width/key encoding.
  • Duplicate pagination boundary: use an exclusive resume start. The Scan end is inclusive unless explicitly changed.
  • Scan ignores limit/time range: those methods currently do not implement the promised constraints. Use client-bounded consumption for counts and a key design for time queries.
  • TypeError: expected bytes, int found with Scan.add_column: current qualifier serialization bug; use family selection plus a qualifier filter.
  • Protobuf Assignment not allowed to message field "time_range": current finite Get time-range path is broken; this is not fixed by changing timestamp units.
  • Get filter appears ignored: that field is not sent. Use a bounded Scan filter.
  • Requested multiple versions but only one value: Row flattens results and loses timestamps/versions.
  • Counter failure after Put(b"0"): a numeric counter is signed 8-byte big-endian, not textual digits. Test on a new counter cell; do not rewrite production values without a migration plan.

More detail: data model, scan guide.

6. Partial writes and retries

A timeout can happen after the server applied a mutation. Do not blindly retry Increment or Append. A conditional mutation returning False means the condition was not met; it is different from an exception with an unknown outcome.

Batch APIs can apply some rows before failing. BufferedMutator clears its pending buffer before sending individual mutations; a failed flush is not a saved retry queue. Retain original input and reconcile writes. An exception listener that returns True continues processing; it does not retry or guarantee delivery.

For Put/Delete, determine whether replay is idempotent for your timestamps, version retention, and concurrent writers. Do not assume every write can be retried safely just because it uses the same row key.

7. Resource growth and topology changes

Close scanners, mutators, tables, and clients in ownership order. Some low-level close/pool paths are incomplete, so monitor file descriptors, memory, and threads during long-running tests. Avoid untested sharing of Client internals across tables or threads.

After truncate or major Region changes, recreate the Client/Table for a clean test. table.invalidate_cache() clears only one cache; it is not a universal recovery operation. If an operation still fails, collect metadata and logs rather than repeatedly mutating topology.

Reporting an issue safely

Include the package version/commit, Python and HBase versions, topology, failing method, sanitized traceback, and the smallest reproducible script. State whether the failure is deterministic and whether any writes may have succeeded.

Remove passwords, tokens, internal customer values, ZooKeeper credentials, and sensitive hostnames before sharing logs. Do not attach a shell exports file or full environment dump. Do not mask failures in reproductions with broad except: pass.