---
title: "ADR-230: An alternative text for one use of an image"
manual: "LLM Foundation"
version: "0.43"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:adr-230@0.43"
source: "Adr/Adr230AnAlternativeTextForOneImageUse.rst"
rendered: "2026-11-04T13:45:29+00:00"
---

# ADR-230: An alternative text for one use of an image {#adr-230}

-   *Status:* Accepted
-   *Date:* 2026-10-25
-   *Amends:* [ADR-198](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-198@0.43) (the rights `replace_file_reference` asks, for a content element that is an inline child), [ADR-212](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-212@0.43) (the rights `attach_file_to_record` asks, for a record that is an inline child)
-   *Authors:* Netresearch DTT GmbH

## Context {#adr-230-context}

An alternative text belongs to one use of an image: the file reference
(`sys_file_reference.alternative`). The file's own text
(`sys_file_metadata.alternative`) is the default every use without a text
of its own falls back to. `set_file_alternative_text`
([ADR-135](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-135@0.43)) writes that default, so it changes the text of
every use of the file that has no text of its own, all at once, and the
writers that touch a reference
([ADR-198](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-198@0.43), [ADR-212](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-212@0.43)) set texts only on a
reference they create.

A site-wide check for missing alternative texts in EXT:nr_mcp_agent (its
ADR-026) lists each use of an image whose effective text is empty, and asks
the assistant for a text per hit. That extension decided that the writer
belongs here, next to `set_file_alternative_text`: every content write the
assistant makes is an nr-llm tool with an approval card, and the tool is
useful without that extension.

## Decision {#adr-230-decision}

`set_file_reference_alternative_text(reference, alternative)` sets
`alternative` on one existing `sys_file_reference`, in place. It keeps
the terms of the other writers ([ADR-134](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-134@0.43),
[ADR-135](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-135@0.43), [ADR-136](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-136@0.43),
[ADR-198](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-198@0.43), [ADR-213](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-213@0.43),
[ADR-214](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-214@0.43)):

-   **One column of one row**, through the DataHandler as the acting backend
    user, in the live workspace only; read back afterwards, and reported as a
    failure when the value did not land.
-   **Any table.** The reference may sit in a `type => file` column of any
    table the TCA declares: a content element, a page's own file field, a
    record of another extension. A table on the read-side denylist, and one
    declared `readOnly` or (for a non-admin) `adminOnly`, is refused.
-   **The reference's own language.** A translated reference is written as
    it is; the tool takes no language argument and changes no other
    language.
-   **The acting user's rights, before the write**: "edit content" on the
    record's page, and for a page's own file field "edit page" as well. The
    form edits a page's file field as a page property, while the DataHandler,
    writing the reference row alone, asks "edit content" on its page (it asks
    "edit page" only when `pages` is in the same datamap), so a page's
    reference needs both. Then: the page's edit lock (`pages.editlock`) for
    a non-admin, which the permission bits do not carry and the DataHandler
    refuses; `tables_modify` for `sys_file_reference`; the record-level
    rights of the record (`tables_modify` on its table, its language,
    `authMode`, the record's own edit lock); the reference's language; and
    the field-level grants for `sys_file_reference:alternative` and for the
    record's file field. A reference or record that is not there, a
    workspace version, a reference outside a file field and a page or record
    the user may not edit share one neutral refusal; a page's file field
    without "edit content", a page edit lock, a missing language right and a
    missing field grant are named.
-   **The owner chain.** Where the record is an inline child (a carousel item
    inside a content element), every record that holds it is asked the same
    questions as the record — table, page, record-level rights — up the chain
    until a record no inline column holds, with the same neutral refusal. The
    DataHandler does not ask this (checked in 13.4.37 and 14.3.10: neither
    `checkRecordEditAccess()` nor the datamap's update check looks at
    inline parents), but where a child sits in a table of its own (a carousel
    item, a content element of a news record) the backend form reaches it only
    through its owners, so an edit lock or a content type outside the
    editor's `authMode` grant on the outer record would otherwise be passed
    by. An owner is any record an `inline` column of **another** table
    points to through the row's `foreign_field`, where its
    `foreign_table_field` and `foreign_match_fields` agree; without a
    `foreign_table_field` every table that holds the uid counts. An inline
    relation of a table to itself is not followed: that is the container
    pattern (EXT:gridelements declares `tt_content.tx_gridelements_children`
    as an inline relation to `tt_content`), where the page module edits each
    child on its own and the DataHandler asks about the child only, so the
    container does not stand between the editor and the child. A pointer no
    claiming table holds (the
    owner is gone) is refused as a gone record is, and a cycle ends at a
    record already asked. The same walk guards the two other writers that
    write a file reference on an existing record, `attach_file_to_record`
    ([ADR-212](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-212@0.43)) and `replace_file_reference`
    ([ADR-198](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-198@0.43); a content element is an inline child of another
    table where an extension such as EXT:news holds content elements); all
    three share it in
    `AsksInlineOwnersTrait`.
-   **Root level.** A record on the root level (pid 0) is written by an admin
    only. A table that sets `security.ignoreRootLevelRestriction` is not
    treated differently; no core table with a file field sets it.
-   **No empty text.** An empty string, and a text of whitespace only, is
    refused. This deviates from `set_file_alternative_text`,
    `attach_file_to_record` and `replace_file_reference`, where `''`
    is a valid value (an explicit empty override, or a decorative image).
    Whether an image is decorative is a decision of its own, which
    EXT:nr_mcp_agent records in a field of its own, and an empty alternative
    text cannot be told apart from a forgotten one.
-   **The card** names the record, its page, the language, the field and the
    file, and shows the reference's own text before and after. Where the
    reference has none (`NULL`), the card says that the file's text applies
    today.
-   **Disabled by default**, in the `editing` group, an idempotent write,
    usable by non-admins.

The read side exists already: `read_records` on `sys_file_reference`
returns `alternative` and keeps `NULL` apart from an empty text.
`get_fal_references` is not extended.

## Consequences {#adr-230-consequences}

-   The assistant can describe one use of an image without changing the text
    of every other use of the same file.
-   The tool has no editor action, like the writers of ADR-198: it is reached
    through the assistant.
-   A use of an image that exists only in a draft workspace cannot be written;
    the check in EXT:nr_mcp_agent overlays workspace rows, this tool does not
    write to them.
-   The three reference writers are stricter than the DataHandler for an
    inline child: an editor whom the DataHandler would let write a carousel
    item's reference is refused while the content element holding the item is
    locked or of a type they may not edit. The first version of this tool, and
    the other two until this record, walked no chain and authorised the
    reference against the child record and its page only.

## Revisit when {#adr-230-revisit}

-   Workspaces are supported by the writers as a whole: this tool follows
    them then.
-   A consumer needs the empty text as a value after all; that is a second
    decision, recorded where the decorative concept lives.
