{"record":{"id":"8e6a0638a3a7e436","repo":"langgenius/dify","slug":"invalid-action-8e6a06","errorCode":"invalid_action","errorMessage":"Document is being indexed, please try again later","messagePattern":"Document is being indexed, please try again later","errorType":"error_code","errorClass":"InvalidActionError","httpStatus":400,"severity":"warning","filePath":"api/controllers/console/datasets/datasets_segments.py","lineNumber":395,"sourceCode":"                model_manager = ModelManager.for_tenant(tenant_id=current_tenant_id)\n                model_manager.get_model_instance(\n                    tenant_id=current_tenant_id,\n                    provider=dataset.embedding_model_provider,\n                    model_type=ModelType.TEXT_EMBEDDING,\n                    model=dataset.embedding_model,\n                )\n            except LLMBadRequestError:\n                raise ProviderNotInitializeError(\n                    \"No Embedding Model available. Please configure a valid provider in the Settings -> Model Provider.\"\n                )\n            except ProviderTokenNotInitError as ex:\n                raise ProviderNotInitializeError(ex.description)\n        segment_ids = request.args.getlist(\"segment_id\")\n\n        document_indexing_cache_key = f\"document_{document.id}_indexing\"\n        cache_result = redis_client.get(document_indexing_cache_key)\n        if cache_result is not None:\n            raise InvalidActionError(\"Document is being indexed, please try again later\")\n        try:\n            SegmentService.update_segments_status(segment_ids, action, dataset, document, session)\n        except Exception as e:\n            raise InvalidActionError(str(e))\n        return SimpleResultResponse(result=\"success\").model_dump(mode=\"json\"), 200\n\n\n@console_ns.route(\"/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segment\")\nclass DatasetDocumentSegmentAddApi(Resource):\n    @console_ns.doc(params=SegmentDocParams.DATASET_DOCUMENT)\n    @setup_required\n    @login_required\n    @account_initialization_required\n    @cloud_edition_billing_resource_check(\"vector_space\")\n    @cloud_edition_billing_knowledge_limit_check(\"add_segment\")\n    @cloud_edition_billing_rate_limit_check(\"knowledge\")\n    @console_ns.expect(console_ns.models[SegmentCreatePayload.__name__])\n    @console_ns.response(200, \"Segment created successfully\", console_ns.models[SegmentDetailResponse.__name__])","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/datasets/datasets_segments.py#L377-L413","documentation":"Returned (HTTP 400, error_code `invalid_action`) by the segment enable/disable endpoint when a Redis flag `document_{document.id}_indexing` is still set. The flag is written while a document is being indexed/re-indexed and acts as a concurrency lock so segment status cannot be mutated mid-index. It is a transient, retryable rejection, not a data problem.","triggerScenarios":"Calling the segment status update endpoint (action=enable|disable) for a document whose indexing job has not finished clearing the `document_{id}_indexing` Redis key. Happens when a user toggles segment enable/disable immediately after uploading/re-indexing a document, or when a previous indexing run crashed and left the key orphaned.","commonSituations":"User re-indexes a document and clicks enable/disable on a segment before the indexing Celery task completes; a stalled/dead indexing worker leaves the Redis key behind so every subsequent toggle fails; Redis was flushed mid-index leaving inconsistent state.","solutions":["Wait for the document indexing job to finish, then retry the same enable/disable request.","If indexing is genuinely done but the error persists, check whether a stale `document_{id}_indexing` key remains in Redis (e.g. `redis-cli GET document_<id>_indexing`) and delete it once you confirm no indexing task is running.","Verify the Celery indexing worker is alive and the document's `indexing_status` is `completed` before retrying."],"exampleFix":"// client: retry with backoff on invalid_action while document is indexing\nasync function toggleSegment(docId, segmentIds, action) {\n  for (let i = 0; i < 5; i++) {\n    const res = await fetch(`/console/api/datasets/${ds}/documents/${docId}/segments?action=${action}`,\n      { method: 'PATCH', body: JSON.stringify({ segment_id: segmentIds }) });\n    if (res.status !== 400) return res;\n    const body = await res.json();\n    if (body.code !== 'invalid_action' || !/indexing/i.test(body.message)) throw body;\n    await new Promise(r => setTimeout(r, 1500 * (i + 1)));\n  }\n  throw new Error('Document still indexing after retries');\n}","handlingStrategy":"retry","validationCode":"# Pre-flight: confirm the document is not actively indexing before toggling segments.\nimport requests\n\ndef safe_update_status(base, headers, dataset_id, document_id, segment_ids, action):\n    doc = requests.get(f\"{base}/console/api/datasets/{dataset_id}/documents/{document_id}\",\n                       headers=headers).json()\n    status = doc.get(\"data\", {}).get(\"indexing_status\")\n    if status and status != \"completed\":\n        raise RuntimeError(f\"document indexing_status={status}; wait and retry\")\n    # optional: check the redis flag is unset via an admin endpoint if exposed\n    return requests.patch(\n        f\"{base}/console/api/datasets/{dataset_id}/documents/{document_id}/segments\",\n        headers=headers,\n        json={\"segment_id\": segment_ids, \"action\": action},\n    )","typeGuard":null,"tryCatchPattern":"# Treat invalid_action+indexing as transient and retry with backoff.\nimport time, requests\n\ndef update_status_with_retry(url, headers, payload, attempts=6, base_delay=1.5):\n    for i in range(attempts):\n        resp = requests.patch(url, headers=headers, json=payload)\n        if resp.status_code == 400:\n            body = resp.json()\n            if body.get(\"code\") == \"invalid_action\" and \"indexing\" in (body.get(\"message\") or \"\").lower():\n                time.sleep(base_delay * (i + 1))\n                continue\n        resp.raise_for_status()\n        return resp\n    raise RuntimeError(\"document still indexing after retries\")","preventionTips":["Surface document indexing_status in the UI and disable the enable/disable toggle while it is not 'completed'.","Ensure the indexing worker reliably clears the `document_{id}_indexing` Redis key on success AND failure.","Add an orphaned-key cleanup that verifies no Celery indexing task is active before deleting stale locks."],"tags":["dify","knowledge-base","segment","redis","concurrency","indexing","transient","rest-api"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}