Error Handling

Common errors, how to detect them, and recommended handling strategies.


Legacy format

Pre-ZIP files use a CArchive binary format. Compatibility samples confirm this container through SketchUp 2020. skppy.load() decodes its envelope and CVersionMap. The generated version matrix verifies loading for save targets from SketchUp 3 through 2020, but an unfamiliar class schema can still be unsupported even when the container and most of the model are otherwise understood.

import skppy

model = skppy.load("file.skp")
if model.legacy_archive is not None:
    print("Legacy archive starts at", model.legacy_archive.archive_offset)

legacy_archive is provenance and diagnostic state. Application code can keep using model.entities, model.materials, and the other shared collections.

Unsupported class layouts raise an explicit parser exception. Re-save those files as SketchUp 2021 or later when conversion is available.

Strict runtime schemas are listed in the legacy format reference. Other known classes are version-aware: their CVersionMap entry and file generation select the reader. An unregistered runtime class is unsupported even when its numeric schema matches a known class.


File not found / permission errors

skppy.load() calls Python’s built-in open(), so standard Python IOError / FileNotFoundError apply:

try:
    model = skppy.load("missing.skp")
except FileNotFoundError as exc:
    print(f"File does not exist: {exc.filename}")
except PermissionError as exc:
    print(f"No read access: {exc.filename}")

Catch these before InvalidSkpError: opening the path failed, so no SKP bytes were available for format validation.


Corrupt or truncated files

When an existing file cannot be decoded as SKP, skppy.load() raises skppy.InvalidSkpError. Its exception cause retains the low-level failure for diagnostics. This gives applications one stable error boundary for malformed modern and legacy input.

Parser-level functions may still raise one of:

Exception

Cause

zipfile.BadZipFile

The embedded ZIP is corrupt.

EOFError

A legacy binary primitive is shorter than expected.

ValueError

A TLV record, string, or required value is malformed.

UnicodeDecodeError

A UTF-8 string payload is invalid.

KeyError

A cross-reference (e.g. vertex ID) does not resolve.

UnsupportedLegacySchemaError

A known legacy class uses an unverified schema.

UnsupportedLegacyObjectError

A legacy runtime class has no reader.

A ZIP container without the required model.dat entry is invalid input and raises InvalidSkpError; it is not treated as a legacy file or allowed to leak the lower-level KeyError from zipfile.

Catch the structured legacy errors separately when a user-facing tool can recommend re-saving the file in a newer SketchUp version:

from skppy.parser_legacy import (
    UnsupportedLegacyObjectError,
    UnsupportedLegacySchemaError,
)

path = "legacy.skp"
try:
    model = skppy.load(path)
except (UnsupportedLegacyObjectError, UnsupportedLegacySchemaError) as exc:
    # This is a valid legacy container beyond the current class coverage.
    print(f"Ask the user to re-save {path!r} in a newer format: {exc}")
except skppy.InvalidSkpError as exc:
    # __cause__ retains the low-level parser failure for logs and bug reports.
    print(f"Could not decode {path!r}: {exc}")
    print(f"Underlying error: {exc.__cause__!r}")

OldFormatError is an internal dispatch signal from the ZIP header reader. A normal skppy.load() call detects the CArchive container first and does not raise it merely because the input is a supported legacy file.


Write/export status

skppy.save() raises NotImplementedError when a shared legacy-only field has no confirmed modern representation. The destination remains untouched because serialization completes in memory first. The Blender exporter reports unsupported Blender object families and conversion warnings in the operator result.

try:
    skppy.save(model, "output.skp")
except NotImplementedError as exc:
    print(f"This model cannot be represented without loss: {exc}")

Validation errors such as duplicate IDs or dangling references are reported as ValueError. In either case, serialization fails before replacing an existing destination.


Common pitfalls

Duplicate material names

If two materials share the same name, model.get_material(name) returns only the first one. Use unique names when creating materials.

Coordinate units

All positions in skppy are in inches. Forgetting to convert can produce models that are 39.37x too large or too small:

INCHES = 1.0
MM = 1.0 / 25.4    # 1 mm in inches
CM = 1.0 / 2.54

vertex = e.add_vertex(500 * MM, 0, 0)   # 500 mm ~= 19.69 inches

Apply the conversion to positions and physical texture scales. Direction vectors, normalized factors, and angles are unitless.

Faces with no vertices

If face.triangulate(entities) returns an empty list, the face is degenerate (fewer than 3 unique non-collinear vertices). This is normal for some SketchUp models that contain zero-area faces used as construction geometry.

Nested instances not flattened

model.entities only contains root-level objects. Nested instances inside definitions are in definition.entities. Always recurse to find all geometry. See guides/components.md for a recursive walker.