Skip to content

Auto-generated thumbnails placed in gathered_assertions (inherited from c2pa-rs) #257

Open

Description

@erik-sv

Summary

When using c2pa-python's Builder API with auto-thumbnail generation enabled
(the default), the resulting C2PA manifest places the claim thumbnail in
gathered_assertions instead of created_assertions. This violates C2PA spec
section 14.3, which requires signer-originated assertions in
created_assertions.

Reproduction

import c2pa
import io, json

signer = c2pa.create_signer(key_pem, cert_pem, "es256", tsa_url)
manifest = {"title": "test.jpg", "assertions": [
    {"label": "c2pa.actions", "data": {"actions": [{"action": "c2pa.created"}]}}
]}
builder = c2pa.Builder(manifest)
dest = io.BytesIO()
builder.sign(signer, "image/jpeg", io.BytesIO(jpeg_bytes), dest)
signer.close()

dest.seek(0)
reader = c2pa.Reader("image/jpeg", dest)
detailed = json.loads(reader.detailed_json())
reader.close()

active = detailed["active_manifest"]
claim = detailed["manifests"][active]["claim"]
print(claim["created_assertions"])   # Missing thumbnail
print(claim["gathered_assertions"])  # Contains thumbnail (wrong)

Root Cause

This is inherited from the underlying c2pa-rs library. See
contentauth/c2pa-rs#2106.

In c2pa-rs/sdk/src/builder.rs, the add_assertion helper for auto-generated
thumbnails is called with created=false, routing them to
gathered_assertions.

Workaround

Option A: Disable auto-thumbnails and add them manually (loses the
convenience of auto-generation):

settings = c2pa.Settings()
settings.set("builder.thumbnail.enabled", "false")

Option B: Use created_assertion_labels in settings (requires a Context):

settings = c2pa.Settings()
settings.set("builder.created_assertion_labels", '["c2pa.thumbnail.claim"]')
context = c2pa.Context(settings=settings, signer=signer)
builder = c2pa.Builder(manifest, context=context)

Neither workaround should be necessary. The library should place its own
auto-generated assertions in created_assertions by default.

Impact

C2PA Conformance Program submissions using c2pa-python will fail review if
thumbnails are enabled. Thumbnails are optional (spec section 17.1), so
disabling them is a valid short-term fix, but conforming implementations
should be able to include thumbnails without manual routing configuration.

Activity

  1. tmathern commented on May 2, 2026

    @tmathern
    Collaborator

    Thanks for the report. I see you also opened contentauth/c2pa-rs#2106 to fix the root cause.

    Therefore I will defer the bug fix to the c2pa-rs repo, and it will go into the Python SDK once the root cause is fixed in the core Rust SDK.

    For now, I will also keep this report open here since you mention workarounds, and to track once a release has a fix for this, leading to a Python SDK update.

  2. tmathern commented on May 2, 2026

    @tmathern
    Collaborator

    For further discussions or details, please add to contentauth/c2pa-rs#2106.

    Having the report in one place makes it much easier for us to track, investigate, and resolve efficiently, so we don't split the discussion. Thank you.

  3. github-jira-sync-bot commented on Jun 24, 2026

    @github-jira-sync-bot

    ✅ Jira issue https://jira.corp.adobe.com/browse/CAI-12741 is successfully created for this GitHub issue.

  4. beeswaxpat commented on Aug 11, 2026

    @beeswaxpat

    Status update with verification on current releases, since this issue is tracking the Python side.

    Reproduced on the latest published release: c2pa-python 0.37.6 (native SDK 0.90.8), Python 3.13, default settings, manifest containing a c2pa.actions assertion with c2pa.created. Result: created_assertions contains only c2pa.hash.data; gathered_assertions contains c2pa.thumbnail.claim (and c2pa.actions.v2, which callers can opt out of per assertion with "created": true).

    The code path is unchanged in c2pa-rs on both the c2pa-v0.90.10 tag (the version currently pinned in c2pa-native-version.txt) and main as of 2026-08-11 (97f27e9). In Builder::to_claim (sdk/src/builder.rs), the auto-generated claim thumbnail is added with created set to false:

    // todo: add setting for created added thumbnails
    add_assertion(&mut claim, &thumbnail, false)?;

    which routes through Claim::add_assertion into Claim::claim_assertion_type (sdk/src/claim.rs). For claim v2, c2pa.thumbnail.claim is not in HASH_LABELS and no created flag is set, so it lands in gathered_assertions unless builder.created_assertion_labels overrides it. The Python package does no assertion routing of its own (it is a ctypes binding over the prebuilt c2pa_c library), so there is nothing to fix in this repo; the fix will arrive via a c2pa-rs release plus a native version bump here, as noted above.

    One caution on the settings workaround documented above: I verified that builder.created_assertion_labels with ["c2pa.thumbnail.claim"] does move the thumbnail into created_assertions on 0.37.6, but it is inserted at created_assertions[0], before the actions assertion. That violates the inception_action_position conformance requirement (spec section 18.14.2), which is exactly what contentauth/c2pa-rs#2238 reports from the same call site. So the workaround trades one conformance finding for another. Until the core fix lands, the option that avoids both findings for generator products is disabling auto thumbnails (builder.thumbnail.enabled = false).

    For the c2pa-rs fix itself (discussion in contentauth/c2pa-rs#2106 per the maintainers): a plain false to true flip at that call site would reproduce the contentauth/c2pa-rs#2238 ordering problem, because the thumbnail block runs before the manifest assertions loop in to_claim. Moving the thumbnail add to after the assertions loop and adding it as a created assertion would resolve both issues at once, optionally gated by a new ThumbnailSettings field defaulting to created, which is what the existing TODO comment anticipates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions