invoke-ai/InvokeAI · error · HTTPException

Not authorized to modify this image

Error message

Not authorized to modify this image

What it means

`assert_image_owner` raises this 403 when a non-admin user attempts to mutate an image they do not own. Ownership passes only if the user owns the image row, owns the image's board, or the image sits on a Public board; otherwise the mutation is refused.

Source

Thrown at invokeai/app/api/routers/_access.py:54

        # aggregates — five extra queries and five extra ways to fail per name.
        #
        # Only a board positively known to be gone falls through to the 403. A storage error
        # propagates instead of being caught here: `board_records.get` deliberately does not
        # translate sqlite errors into not-found, and a caller that cannot decide ownership
        # must not report the name as an ordinary permission denial — the batch loops treat a
        # 403 as a silent auth skip, which turned a locked database into images dropped from
        # the response with no failure reported at all.
        try:
            board = ApiDependencies.invoker.services.board_records.get(board_id)
        except BoardRecordNotFoundException:
            pass
        else:
            if board.user_id == current_user.user_id:
                return
            if board.board_visibility == BoardVisibility.Public:
                return

    raise HTTPException(status_code=403, detail="Not authorized to modify this image")


def _assert_image_record_exists(image_name: str) -> None:
    """Turn a refusal into a 404 when the image is positively gone.

    The two refusals mean opposite things to a client holding a reference to the image — a
    workflow's image field, a reference image on a canvas layer. Gone is permanent, and the
    reference should be dropped. Denied is a permission decision that can be reversed (a board
    flipped back to Shared, an owner re-granting access), and dropping the reference over one
    destroys work the user cannot get back by restoring the permission.

    Nothing above can tell them apart: the ownership test rests on `images.user_id`, which is
    gone with the row, so a deleted image reaches that same 403 as a foreign one. So the
    distinction is made here, on the refusal path only — the happy path pays nothing for it.

    A storage error propagates rather than answering either, so an unreadable database cannot
    present as a deleted image and take the user's references down with it. `exists` is a bare
    row probe rather than `get` for the same reason from the other side: `get` deserializes, so

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Log in as the image's owner or an admin user to perform the mutation
  2. Move the image to a Public board (as its owner) so other users gain mutation rights on it
  3. As the board owner, take ownership of the image's board so you qualify as the owner
  4. Verify the image still exists and its board record is intact; if the board was deleted, re-create/restore it
Defensive patterns

Strategy: try-catch

Validate before calling

// Before mutating, check read access/ownership if the API exposes it
const img = await fetch(`/v1/images/i/${imageName}/metadata`, {headers: authHeaders});
if (!img.ok && img.status === 403) console.warn('Skipping image (not owned):', imageName);

Type guard

function canModifyImage(image, user) {
  return user.is_admin || image.user_id === user.user_id || image.board_visibility === 'public';
}

Try / catch

try {
  await fetch(`/v1/images/i/${imageName}`, { method: 'DELETE', headers: authHeaders });
} catch (e) {
  if (e.status === 403) { /* per-image skip, continue batch */ }
  else if (e.status === 404) { /* drop stale reference */ }
  else throw e;
}

Prevention

When it happens

Trigger: PATCH/DELETE on an image (e.g. /v1/images/i/{image_name}, star, update board) where current_user is not admin, not the image owner, does not own the image's board, and the board is not Public. Also fires if the image's board record is missing (BoardRecordNotFoundException falls through).

Common situations: A multiuser instance where a collaborator tries to delete or update another user's canvas image; an image whose board was deleted so ownership cannot be established; stale UI state referencing an image moved between boards.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/ac69aa29e1364882. Report an issue: GitHub.