Errors¶
Everything the SDK raises derives from QuickbaseError. Errors that mean a request wasn't sent come first, then the ones for a request that failed. A timed-out write may still have been applied.
The examples below use tasks, made as shown here, and app, which is qb.app("bq1234567"). Job Tasks has Title, Status, Hours, Due Date, Code, which is unique, and Attachment. It starts with three records, and each example starts from those again. A live test runs every example with both clients and checks its output.
from pyquickbase import Quickbase
with Quickbase(
realm="acme", token="your_user_token"
) as qb:
tasks = qb.app("bq1234567").table("Job Tasks")
from pyquickbase import AsyncQuickbase
async with AsyncQuickbase(
realm="acme", token="your_user_token"
) as qb:
tasks = qb.app("bq1234567").table("Job Tasks")
The base class¶
For the full picture, see Handling errors.
QuickbaseError¶
Base class for every exception this library defines.
Example
from pyquickbase import QuickbaseError
try:
tasks.get(1, select=["No Such Field"])
except QuickbaseError as error:
print(type(error).__name__)
from pyquickbase import QuickbaseError
try:
await tasks.get(1, select=["No Such Field"])
except QuickbaseError as error:
print(type(error).__name__)
Output
UnknownFieldError
Mistakes caught before sending¶
For the full picture, see Handling errors.
Example
from pyquickbase import ConfigurationError
try:
qb.app("not an app ID")
except ConfigurationError as error:
print(error)
from pyquickbase import ConfigurationError
try:
qb.app("not an app ID")
except ConfigurationError as error:
print(error)
Output
An app ID contains only letters and numbers.
QueryError¶
A query can't be sent as written, so it wasn't sent to Quickbase.
Inherits from QuickbaseError
Example
from pyquickbase import QueryError
try:
tasks.count("{7.ex.'Open'}")
except QueryError:
print("A lowercase operator was refused")
from pyquickbase import QueryError
try:
await tasks.count("{7.ex.'Open'}")
except QueryError:
print("A lowercase operator was refused")
Output
A lowercase operator was refused
TooManyRecordsError¶
More records match a summary than its max_records_scanned lets it read. None were read.
A summary that only counts reads no records, so this is raised only for one that groups, totals, averages, or finds the lowest or highest value.
Inherits from QueryError
Attributes
matchedint
How many records match.limitint
The most records the summary may read.
Example
from pyquickbase import TooManyRecordsError
try:
tasks.summarize(
"Status", sum="Hours", max_records_scanned=1
)
except TooManyRecordsError as error:
print(error.matched > error.limit, error.limit)
from pyquickbase import TooManyRecordsError
try:
await tasks.summarize(
"Status", sum="Hours", max_records_scanned=1
)
except TooManyRecordsError as error:
print(error.matched > error.limit, error.limit)
Output
True 1
ConfirmationError¶
A change needs the confirmation its preview or plan returned, and this one is missing, expired, or for something else. Nothing was changed. Preview or plan again, and show the person the result.
Inherits from QuickbaseError
Example
from pyquickbase import ConfirmationError
update = tasks.operation("update", require_preview=True)
try:
update.run(
{"records": [{"Record ID#": 1, "Status": "Done"}]}
)
except ConfirmationError:
print("Preview it first")
from pyquickbase import ConfirmationError
update = await tasks.operation("update", require_preview=True)
try:
await update.run(
{"records": [{"Record ID#": 1, "Status": "Done"}]}
)
except ConfirmationError:
print("Preview it first")
Output
Preview it first
InvalidValueError¶
A write can't be sent as written, so it wasn't sent to Quickbase.
Inherits from QuickbaseError
Example
from pyquickbase import InvalidValueError
try:
tasks.insert([{"Record ID#": 1, "Title": "Oops"}])
except InvalidValueError:
print("Nothing was written")
from pyquickbase import InvalidValueError
try:
await tasks.insert([{"Record ID#": 1, "Title": "Oops"}])
except InvalidValueError:
print("Nothing was written")
Output
Nothing was written
FileTooLargeError¶
A file is over a limit on how much of it may be read, so reading stopped.
Inherits from InvalidValueError
Attributes
limitint
The limit it went over.unitLiteral['rows', 'bytes']
Whatlimitcounts:rows, formax_rows, orbytes, formax_unzipped_bytes.
Example
import io
from pyquickbase import FileTooLargeError
try:
tasks.rows_from_file(
io.StringIO("Title\nOne\nTwo\n"), max_rows=1
)
except FileTooLargeError as error:
print(error.limit, error.unit)
import io
from pyquickbase import FileTooLargeError
try:
await tasks.rows_from_file(
io.StringIO("Title\nOne\nTwo\n"), max_rows=1
)
except FileTooLargeError as error:
print(error.limit, error.unit)
Output
1 rows
PolicyRefusedError¶
The client's policy doesn't allow this request, so it wasn't sent.
The message says what was refused, by name, which rule refused it, and what would allow it, without repeating any values from the request. Quickbase's own permissions still apply to whatever the policy allows. A field the policy hides raises UnknownFieldError instead, as if it didn't exist.
Inherits from QuickbaseError
Attributes
decisionAny
TheDecision, with the rule, the reason, and the tables and fields involved.
Example
from pyquickbase import (
Quickbase,
Policy,
PolicyRefusedError,
)
with Quickbase.from_env(
policy=Policy.read_only()
) as qb:
tasks = qb.app("bq1234567").table("bqtasks123")
try:
tasks.insert([{"Title": "Paint walls"}])
except PolicyRefusedError as error:
print(error.decision.verb, error.decision.layer)
print(error.decision.reason)
from pyquickbase import (
AsyncQuickbase,
Policy,
PolicyRefusedError,
)
async with AsyncQuickbase.from_env(
policy=Policy.read_only()
) as qb:
tasks = qb.app("bq1234567").table("bqtasks123")
try:
await tasks.insert([{"Title": "Paint walls"}])
except PolicyRefusedError as error:
print(error.decision.verb, error.decision.layer)
print(error.decision.reason)
Output
insert policy
No rule allows it
Names that don't match¶
For the full picture, see Handling errors.
Example
from pyquickbase import UnknownTableError
try:
app.table("Job Task").info()
except UnknownTableError:
print("There's no such table")
from pyquickbase import UnknownTableError
try:
await app.table("Job Task").info()
except UnknownTableError:
print("There's no such table")
Output
There's no such table
Example
from pyquickbase import F, UnknownFieldError
try:
tasks.count(F("Statuss") == "Open")
except UnknownFieldError as error:
print(error)
from pyquickbase import F, UnknownFieldError
try:
await tasks.count(F("Statuss") == "Open")
except UnknownFieldError as error:
print(error)
Output
This table has no field labeled 'Statuss', or this client can't see it. Did you mean 'Status'?
UnknownReportError¶
The table has no report with that name or ID, or several reports share the name.
Inherits from QuickbaseError
Example
from pyquickbase import UnknownReportError
try:
tasks.report("No Such Report").info()
except UnknownReportError:
print("There's no such report")
from pyquickbase import UnknownReportError
try:
await tasks.report("No Such Report").info()
except UnknownReportError:
print("There's no such report")
Output
There's no such report
UnsupportedReportError¶
The report can't be read as records, such as a summary or chart report.
Inherits from QuickbaseError
Example
from pyquickbase import UnsupportedReportError
try:
tasks.report("List All").summary_rows()
except UnsupportedReportError:
print("That's a table report, so use run()")
from pyquickbase import UnsupportedReportError
try:
await tasks.report("List All").summary_rows()
except UnsupportedReportError:
print("That's a table report, so use run()")
Output
That's a table report, so use run()
SchemaMismatchError¶
A table doesn't match what the code expects, so nothing was changed.
An ensure_ method found a field that isn't what it was asked for, or the schema changed since a plan was made.
Inherits from QuickbaseError
Example
from pyquickbase import SchemaMismatchError
try:
tasks.ensure_field("Title", "numeric")
except SchemaMismatchError:
print("Title isn't numeric, and wasn't changed")
from pyquickbase import SchemaMismatchError
try:
await tasks.ensure_field("Title", "numeric")
except SchemaMismatchError:
print("Title isn't numeric, and wasn't changed")
Output
Title isn't numeric, and wasn't changed
Writes¶
For the full picture, see Writing records.
WriteError¶
Part of a write failed, and the rest may have been applied.
When a later batch of a large write fails, __cause__ holds that batch's own error, such as a RateLimitError or RequestTimeoutError.
Inherits from QuickbaseError
Attributes
resultAny
AWriteResultwith the records that were written and the rows that failed, when Quickbase reported them.
Example
from pyquickbase import WriteError
try:
tasks.insert([{"Title": "Repeat", "Code": "T-1"}])
except WriteError as error:
print(error.result.errors[0].reason)
from pyquickbase import WriteError
try:
await tasks.insert([{"Title": "Repeat", "Code": "T-1"}])
except WriteError as error:
print(error.result.errors[0].reason)
Output
duplicate
WritePermissionError¶
A write was refused because of the token's role, so nothing in the failed rows changed.
Quickbase refuses some writes with a 403, and reports others as row errors in a successful response, depending on the role. Both raise this, so except PermissionDeniedError and except WriteError each catch it. result holds what earlier batches or rows did write. If some rows failed for other reasons as well, you get a plain WriteError, whose rows each have a kind.
Inherits from WriteError, PermissionDeniedError
Attributes
status_codeint
403 for a refused request, and 207 for row errors.
class WritePermissionError(WriteError, PermissionDeniedError)
SchemaChangeError¶
A change in a schema plan failed, so the ones after it weren't attempted.
Quickbase can't undo the changes before it, so result says which were made. Planning the same changes again shows only what's left, as the ones made count as already done.
Inherits from WriteError
Attributes
resultAny
ASchemaChangeResult.
class SchemaChangeError(WriteError)
Responses from Quickbase¶
For the full picture, see Handling errors.
APIError¶
Quickbase answered with an unsuccessful status code.
This is the base of the errors for each status, so it's what you catch for any of them.
Inherits from QuickbaseError
Attributes
status_codeint
The HTTP status.methodstr
The request's HTTP method.pathstr
The request's path.messagestr | None
Quickbase's wording, when it sent any. It can contain HTML, so escape it before showing it in a web page.descriptionstr | None
Quickbase's further explanation, when it sent any.request_idstr | None
Include this when reporting a problem to Quickbase support.
Example
from pyquickbase import APIError
try:
qb.request(
"GET",
"/tables/bzzzzzzzz",
params={"appId": "bq1234567"},
)
except APIError as error:
print(error.status_code, error.method)
print(error.message)
from pyquickbase import APIError
try:
await qb.request(
"GET",
"/tables/bzzzzzzzz",
params={"appId": "bq1234567"},
)
except APIError as error:
print(error.status_code, error.method)
print(error.message)
Output
404 GET
Invalid DBID
Example
from pyquickbase import BadRequestError
try:
app.create_table("Job Tasks")
except BadRequestError as error:
print(error.status_code)
from pyquickbase import BadRequestError
try:
await app.create_table("Job Tasks")
except BadRequestError as error:
print(error.status_code)
Output
400
AuthenticationError¶
401: the token is invalid, or the app or table doesn't exist or is out of the token's reach.
Quickbase gives the same answer for all of these, so this can't say which one happened.
Inherits from APIError
Example
from pyquickbase import AuthenticationError
try:
qb.app("bzzzzzzzz").info()
except AuthenticationError as error:
print(error.status_code)
print(error.message)
from pyquickbase import AuthenticationError
try:
await qb.app("bzzzzzzzz").info()
except AuthenticationError as error:
print(error.status_code)
print(error.message)
Output
401
Access denied
class PermissionDeniedError(APIError)
NotFoundError¶
404: what was asked for doesn't exist. An app that doesn't exist is a 401 instead.
Inherits from APIError
Example
from pyquickbase import NotFoundError
try:
tasks.download_file(999, "Attachment")
except NotFoundError as error:
print(error.status_code)
from pyquickbase import NotFoundError
try:
await tasks.download_file(999, "Attachment")
except NotFoundError as error:
print(error.status_code)
Output
404
RateLimitError¶
429: retries ran out, or Quickbase asked for a longer wait than the retry policy allows.
Inherits from APIError
Attributes
retry_afterfloat | None
The wait Quickbase asked for, in seconds, if it did.
class RateLimitError(APIError)
class ServerError(APIError)
Connection failures¶
For the full picture, see Handling errors.
class NetworkError(QuickbaseError)
RequestTimeoutError¶
The request timed out. A timed-out write may still have been applied.
Inherits from NetworkError
Example
from pyquickbase import (
Quickbase,
NetworkError,
RetryPolicy,
)
no_retries = RetryPolicy(max_retries=0)
with Quickbase.from_env(
timeout=0.0001, retry=no_retries
) as qb:
try:
qb.app("bq1234567").tables()
except NetworkError as error:
print(type(error).__name__)
from pyquickbase import (
AsyncQuickbase,
NetworkError,
RetryPolicy,
)
no_retries = RetryPolicy(max_retries=0)
async with AsyncQuickbase.from_env(
timeout=0.0001, retry=no_retries
) as qb:
try:
await qb.app("bq1234567").tables()
except NetworkError as error:
print(type(error).__name__)
Output
RequestTimeoutError
InvalidResponseError¶
Quickbase reported success, but the response body wasn't in the expected format.
Inherits from QuickbaseError
class InvalidResponseError(QuickbaseError)