---
title: "ADR-212: A generic file attacher, only where no narrow writer exists"
manual: "LLM Foundation"
version: "0.39"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:adr-212@0.39"
source: "Adr/Adr212AGenericFileAttacherWhereNoNarrowWriterExists.rst"
rendered: "2026-10-07T21:08:05+00:00"
---

# ADR-212: A generic file attacher, only where no narrow writer exists {#adr-212}

-   *Status:* Accepted
-   *Date:* 2026-10-06
-   *Authors:* Netresearch DTT GmbH

## Context {#adr-212-context}

A live editorial run on the Netresearch demo (NEXT-199) created a news draft
with `create_news_draft` and could not give it its image: "the image was not
attached". [ADR-197](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-197@0.39) lets the assistant create a record in a
table no narrow writer covers, and it cannot set a file field by design (a
relation is not a scalar column). `attach_file_to_content_element` references
an existing file from a content element and from nothing else.

The same position as in ADR-197 applies: an extension that wants its records
written brings its own writer, and a writer for the file field of one extension
is reviewed where that extension's TCA is known. Until it exists, every
extension table with a file field has the gap this record closes.

## Decision {#adr-212-decision}

\``attach_file_to_record(table, record, file, field?, title?, alternative?,
description?)`` references one existing ``sys_file`\` from one file field of one
record. It is the sibling of `attach_file_to_content_element` and keeps its
terms (ADR-135, ADR-146, ADR-180):

-   **One datamap.** One `sys_file_reference` through the DataHandler as the
    acting backend user, in the live workspace, with the new reference's
    placeholder in the parent list, so the record's counter and the reference's
    position come out right in one pass. The read-back asserts row, counter,
    position and every text, and a failed read-back deletes the reference and
    writes the parent list back.
-   **No upload, move or rename.** The file must already exist in a storage the
    `FalStorageGate` allows and inside the acting user's file mounts.
-   **The record may be hidden.** That is the draft the assistant has just
    created; the tool never changes `hidden`.
-   **The acting user's rights, before the write.** `tables_modify` on the
    table, content edit on the record's page, the field-level grant for the file
    field (the DataHandler would drop it in silence), and the neutral refusal
    "Record or file not found, or not permitted." for an absent record, an
    unreachable file and a page the user may not edit alike.
-   **Tables by exclusion.** `pages` and `tt_content` are refused by name and
    point to `set_page_social_image` and `attach_file_to_content_element`;
    `sys_*` tables, the read-side denylist and a table declared `adminOnly`,
    `hideTable` or `readOnly` are out.
-   **A file field of the table, and an extension it accepts.** The field must be
    a `type => file` column; `allowed` and `disallowed` of that column
    (with the three `common-*` aliases resolved, and as narrowed by the record
    type's `columnsOverrides`) are applied to the file.
    Omitting `field` is accepted only where the table has exactly one.
-   **Default language only**, as the sibling does.
-   **Disabled by default**, in the `editing` group, a non-idempotent write.

Copyright is not a column of `sys_file_reference`: it is a property of the
file (`sys_file_metadata.copyright`, EXT:filemetadata) and is written by
`update_fal_asset_meta`. This tool sets what belongs to the reference — title,
alternative text, description — which override the file's for this one place.

Whether an attached image becomes the record's social image is the
extension's rule. EXT:news prints the first reference with `showinpreview`,
and otherwise the first of `fal_media`, as `og:image`.

## Consequences {#adr-212-consequences}

-   The assistant can complete a news article from one conversation: draft
    (`create_news_draft`), image (this tool, `fal_media`), the image's
    metadata (`update_fal_asset_meta`), and read the draft back
    (`read_records` with `include_hidden`).
-   The tool has no editor action: an editor action is offered on a record of one
    table (ADR-152), and this tool has no such subject. It is reached through the
    assistant only.
-   A table whose installation excludes it from `create_record_draft` is not
    excluded here; the exclusion list of ADR-197 is a creation rule.

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

-   EXT:news, or a bridge on its behalf, ships its own writer for `fal_media`:
    this tool then steps back for that table, the way `create_record_draft`
    does for a table another registered tool creates records in.
-   A `showinpreview` argument is wanted. Its TCA type depends on the news
    setting `advancedMediaPreview` (a checkbox, or a select of three values),
    so the argument and its read-back have to follow whichever is installed.
