langgenius/dify · info · DocumentAlreadyFinishedError

document_already_finished

document_already_finished

Error message

The document has been processed. Please refresh the page or go to the document details.

What it means

DocumentAlreadyFinishedError (HTTP 400, error_code=document_already_finished) raised in GET /datasets/{dataset_id}/documents/{document_id}/indexing-estimate when document.indexing_status is in {COMPLETED, ERROR}. Estimating cost for a finished document is meaningless, so the controller refuses.

Source

Thrown at api/controllers/console/datasets/datasets_document.py:713

        "Indexing estimate calculated successfully",
        console_ns.models[IndexingEstimateResponse.__name__],
    )
    @console_ns.response(404, "Document not found")
    @console_ns.response(400, "Document already finished")
    @setup_required
    @login_required
    @account_initialization_required
    @with_current_user
    @with_current_tenant_id
    @rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.DATASET_CREATE_AND_MANAGEMENT)
    @with_session
    def get(self, session: Session, current_tenant_id: str, current_user: Account, dataset_id: UUID, document_id: UUID):
        dataset_id_str = str(dataset_id)
        document_id_str = str(document_id)
        document = self.get_document(session, dataset_id_str, document_id_str, current_user, current_tenant_id)

        if document.indexing_status in {IndexingStatus.COMPLETED, IndexingStatus.ERROR}:
            raise DocumentAlreadyFinishedError()

        data_process_rule = document.get_dataset_process_rule(session=session)
        data_process_rule_dict: Mapping[str, Any] = data_process_rule.to_dict() if data_process_rule else {}

        if document.data_source_type == "upload_file":
            data_source_info = document.data_source_info_dict
            if data_source_info and "upload_file_id" in data_source_info:
                file_id = data_source_info["upload_file_id"]

                file = session.scalar(
                    select(UploadFile)
                    .where(UploadFile.tenant_id == document.tenant_id, UploadFile.id == file_id)
                    .limit(1)
                )

                # raise error if file not found
                if not file:
                    raise NotFound("File not found.")

View on GitHub (pinned to ef8544b173)

Solutions

  1. Refresh the document details page to reflect terminal status.
  2. Don't call the estimate endpoint once indexing_status is completed/error.
  3. Check indexing_status via the documents API before requesting an estimate.
  4. If you need post-hoc stats, read the document segments / token counts instead of the estimate endpoint.
Defensive patterns

Strategy: validation

Validate before calling

import requests

def document_is_inflight(base_url, headers, dataset_id, document_id) -> bool:
    r = requests.get(
        f"{base_url}/console/api/datasets/{dataset_id}/documents/{document_id}/indexing-status",
        headers=headers,
    )
    r.raise_for_status()
    return r.json().get('indexing_status') not in {'completed', 'error'}

if not document_is_inflight(base, hdrs, dataset_id, document_id):
    raise SystemExit('document already finished; estimate is not meaningful')

Type guard

def is_inflight(status: str) -> bool:
    return status not in {'completed', 'error'}

Try / catch

try:
    r = requests.get(f"{base}/console/api/datasets/{dataset_id}/documents/{document_id}/indexing-estimate",
                     headers=hdrs)
    r.raise_for_status()
except requests.HTTPError as e:
    if (e.response.json() or {}).get('code') == 'document_already_finished':
        # read segment counts instead; do not retry estimate
        r = requests.get(f"{base}/console/api/datasets/{dataset_id}/documents/{document_id}/segments",
                         headers=hdrs)
    else:
        raise

Prevention

When it happens

Trigger: GET .../documents/{document_id}/indexing-estimate on a document whose indexing already completed or errored out.

Common situations: Stale UI still showing an 'Estimate' button after processing finished; polling retry; double submission after a long wait.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/8e145dae65d9f7b9. Report an issue: GitHub.