langgenius/dify · error · ValueError
doc_metadata must be a dictionary.
Error message
doc_metadata must be a dictionary.
What it means
Raised in the metadata PUT handler when doc_metadata is not a dict (datasets_document.py:1314). DocumentMetadataUpdatePayload types doc_metadata as Any, so the route accepts any JSON value and the isinstance(doc_metadata, dict) check fails post-parse. Same controller defect: ValueError becomes HTTP 500, not 400.
Source
Thrown at api/controllers/console/datasets/datasets_document.py:1315
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)
doc_type = req_data.doc_type
doc_metadata = req_data.doc_metadata
# The role of the current user in the ta table must be admin, owner, dataset_operator, or editor
if not current_user.is_dataset_editor:
raise Forbidden()
if doc_type is None or doc_metadata is None:
raise ValueError("Both doc_type and doc_metadata must be provided.")
if doc_type not in DocumentService.DOCUMENT_METADATA_SCHEMA:
raise ValueError("Invalid doc_type.")
if not isinstance(doc_metadata, dict):
raise ValueError("doc_metadata must be a dictionary.")
metadata_schema: dict[str, Any] = cast(dict[str, Any], DocumentService.DOCUMENT_METADATA_SCHEMA[doc_type])
document.doc_metadata = {}
if doc_type == "others":
document.doc_metadata = doc_metadata
else:
for key, value_type in metadata_schema.items():
value = doc_metadata.get(key)
if value is not None and isinstance(value, value_type):
document.doc_metadata[key] = value
document.doc_type = doc_type
document.updated_at = naive_utc_now()
return SimpleResultMessageResponse(result="success", message="Document metadata updated.").model_dump(
mode="json"
), 200
View on GitHub (pinned to ef8544b173)
Solutions
- Send doc_metadata as a JSON object (dict) whose keys/values match the doc_type schema.
- Server-side fix: type the payload field as dict[str, Any] so Pydantic rejects non-objects with a 422.
Example fix
# before
class DocumentMetadataUpdatePayload(BaseModel):
doc_type: str | None = None
doc_metadata: Any = None
# after
class DocumentMetadataUpdatePayload(BaseModel):
doc_type: str
doc_metadata: dict[str, Any] Defensive patterns
Strategy: type-guard
Validate before calling
if not isinstance(doc_metadata, dict):
raise TypeError("doc_metadata must be a JSON object (dict), got " + type(doc_metadata).__name__) Type guard
def is_metadata_dict(doc_metadata) -> bool:
return isinstance(doc_metadata, dict) Try / catch
try:
console.update_document_metadata(dataset_id, document_id, payload)
except HTTPError as e:
if e.response.status_code >= 500 and "must be a dictionary" in e.response.json().get("message", ""):
payload["doc_metadata"] = dict(payload["doc_metadata"]) # coerce and retry
console.update_document_metadata(dataset_id, document_id, payload)
else:
raise Prevention
- Serialize doc_metadata as a JSON object, never a string or list.
- Add a client-side isinstance check before sending.
When it happens
Trigger: PUT .../metadata with doc_metadata as a JSON string ("title=foo"), a list, a number, or null; the isinstance check at line 1314 rejects it.
Common situations: Client serializing the metadata object to a string before sending; sending an array of key-value pairs instead of an object; SDK generated with the wrong field type.
Related errors
- Both doc_type and doc_metadata must be provided.
- Invalid doc_type.
- invalid_metadata
- ${name} must be a non-empty array
- streaming response body missing
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/b573d9c3039b2b9b.
Report an issue: GitHub.