fedify icon indicating copy to clipboard operation
fedify copied to clipboard

Fedify fails to handle URL strings for `icon` property

Open allouis opened this issue 3 months ago • 5 comments

Summary

Fedify throws a JSON parsing error when processing ActivityPub objects where the icon property is a URL string instead of an Image object. This is valid according to ActivityStreams 2.0 specification but causes getIcon() to fail.

Expected Behavior

Fedify should handle the string and convert it into either an Image or a Link object

Both are valid per the ActivityStreams 2.0 specification.

Actual Behavior

When calling getIcon() on a Person object that has a direct URL string for icon, Fedify attempts to parse the PNG file content as JSON, resulting in a syntax error.

Reproducing

Here's an example Actor object that causes the issue:

  • https://links.bouncepaw.com/@bouncepaw
Full JSON-LD

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1"
  ],
  "followers": "https://links.bouncepaw.com/followers",
  "following": "https://links.bouncepaw.com/following",
  "icon": "https://links.bouncepaw.com/static/pix/favicon.png",
  "id": "https://links.bouncepaw.com/@bouncepaw",
  "inbox": "https://links.bouncepaw.com/inbox",
  "name": "bouncepaw's links",
  "outbox": "https://links.bouncepaw.com/outbox",
  "preferredUsername": "bouncepaw",
  "publicKey": {
    "id": "https://links.bouncepaw.com/@bouncepaw#main-key",
    "owner": "https://links.bouncepaw.com/@bouncepaw",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoID5jG9sKf6Dq0jHmxK4\ndHnDR+mTYEojzpW0WjSRLsbqQ3w1jxNXkvHeiSvNFFZy7AJ1X04k8yUsm3iWAMNS\n+2obgK2ZegsYx85Q2DGLJxv3H2MCAtKUeN6MMEVOPaSqFuEgAph7wsFMvoCHqVVV\nw/Kxp0zl2/YBOCRVZqdzwvgYJAm2MTKB/58tIkuSLV487/LTs/qShRyZATKVhtQf\n4goLVtI4SjcULlV6yUYC4wDMiJAXOLxX0WPxF6yupMuMKNRJcamHKSGgzkZiBHxZ\nsla2GOBdUZ1NYIv2RuIeWKmFK4hhYzJhAvgvGogiOTSR77b7Aa/ngdkYczKf5w7S\nxwIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  "summary": "Bookmarks and whatnot. Закладки и всякое.",
  "type": "Person",
  "url": "https://links.bouncepaw.com/@bouncepaw"
}

You can reproduce the error by running:

const fedify = require('@fedify/fedify');
const person = await fedify.lookupObject('https://links.bouncepaw.com/@bouncepaw');
const icon = await person.getIcon(); // This errors

Error Details

Uncaught SyntaxError: Unexpected token '�', "�PNG
"... is not valid JSON

The error occurs because Fedify is trying to parse the PNG file content (which starts with the PNG magic bytes) as JSON.

Thoughts

The ActivityStreams vocabulary defines the "Range" icon as being Link or Image here https://www.w3.org/TR/activitystreams-vocabulary/#dfn-icon

The icon property in Fedify is only defined as Image - so the URL is being resolved and tried to turn into an Image. I wonder if expanding the range in Fedify to include Link would fix this? I'm not sure because I don't have a lot of context on how the codegen works

Environment

  • Fedify version: 1.8.8
  • Node.js version: v22.14.0
  • OS: Alpine Linux (Docker)

allouis avatar Sep 10 '25 13:09 allouis

I'd like to offer a perspective on this from the JSON-LD and ActivityStreams specification standpoint.

The spec vs. reality problem

From a strict interpretation of the ActivityStreams vocabulary:

  1. icon Range: The icon property has a range of Image | Link
  2. Image Type: Image extends Document extends Object, meaning it can contain rich metadata like name, summary, mediaType, etc.
  3. JSON-LD @id References: In the JSON-LD context, icon is defined with "@type": "@id", meaning the string value should reference a structured object

So technically, "icon": "https://links.bouncepaw.com/static/pix/favicon.png" (when requested with Accept: application/activity+json) should return a JSON-LD Image object like:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Image", 
  "id": "https://links.bouncepaw.com/static/pix/favicon.png",
  "name": "bouncepaw's favicon",
  "url": "https://links.bouncepaw.com/static/pix/favicon.png",
  "mediaType": "image/png"
}

The pragmatic solution

While this case appears to be an incorrect implementation (most implementations either use proper Image objects or simply ignore direct image URLs in icon properties), from a practical standpoint, a JSON-LD processor can't be expected to handle every possible resource type that an @id might reference.

I think the right approach would be:

  1. First attempt: Try to retrieve and parse as ActivityStreams JSON-LD
  2. Fallback: If parsing fails due to non-JSON content, handle it gracefully by ignoring the icon silently

This way Fedify can stay spec-compliant for correct implementations while being robust against edge cases like this.

What do you think about this approach?

dahlia avatar Sep 10 '25 16:09 dahlia

The ActivityStreams specification explicitly supports the pattern we're seeing. Looking at the ActivityStreams Core specification, Example 11 shows exactly this usage:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Application", 
  "image": "http://example.org/application/123.png"
}

Additionally, the W3C maintains specific guidance on this pattern:

"To maximize interoperability, consumers should treat any property that has a range of Object and Link and a value of a string as follows: Absent other information, assume that a bare string is the href of a Link object, not an id"

This indicates that "icon": "https://example.com/image.png" should be treated as a Link object with that URL as its href, not as an attempt to dereference an ActivityStreams document.

So rather than treating this as an "incorrect implementation" that needs graceful fallback handling, IMO Fedify should support this as a first-class use case. The proper behavior would be:

  1. String value: Treat as a Link object with the string as the href
  2. Object value: Process normally as Image/Link object
  3. Only attempt JSON-LD dereferencing: When the value is explicitly an @id reference in an object

This aligns with both the specification's intent and real-world ActivityPub federation practices. What do you think about implementing this as the primary behavior rather than a fallback?

allouis avatar Sep 11 '25 10:09 allouis

Ah, I see what you mean—this does highlight an interesting inconsistency that I missed.

There seems to be a conflict between:

  1. JSON-LD context definition:

    "icon": {
      "@id": "as:icon",
      "@type": "@id"
    }
    

    This tells JSON-LD processors to treat string values as IRI references (attempting dereferencing).

  2. ActivityStreams Core guidance (as you cited):

    "assume that a bare string is the href of a Link object, not an id"

    This suggests string values should be treated as Link hrefs (no dereferencing).

The implementation challenge

This creates a real challenge for Fedify, which relies on JSON-LD processing. When the JSON-LD context defines @type: "@id", the JSON-LD library normalizes string values to object @id fields—there's no easy way to override this behavior at the Fedify level.

It seems like this might be a specification-level inconsistency rather than just an implementation issue.

Potential paths forward

  1. Fix the JSON-LD context: Remove @type: "@id" for properties that should support bare URLs
  2. Clarify the specifications: Resolve the inconsistency at the spec level

@ThisIsMissEm I'd be curious to hear your thoughts on how this should be handled.

dahlia avatar Sep 11 '25 14:09 dahlia

These would be best opened as issues for w3c/activitypub or w3c/activitystreams repos. But there's a lot of these inconsistencies that exist.

ThisIsMissEm avatar Sep 11 '25 16:09 ThisIsMissEm

Thanks @ThisIsMissEm for pointing us in the right direction!

I found that there's already a related issue at w3c/activitystreams#595 that addresses the same inconsistency between the JSON-LD context and the specification text. I've added a comment there noting that icon and image properties have the same problem as href.

It looks like this is indeed a broader specification-level issue that needs to be resolved upstream.

dahlia avatar Sep 11 '25 17:09 dahlia