{"record":{"id":"5439f8346df929bf","repo":"Tencent/WeKnora","slug":"bind-stored-resource-w","errorCode":null,"errorMessage":"bind stored resource: %w","messagePattern":"bind stored resource: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/resource_catalog.go","lineNumber":104,"sourceCode":"\nfunc (s *resourceCatalogFileService) SaveFile(\n\tctx context.Context,\n\tfile *multipart.FileHeader,\n\ttenantID uint64,\n\tknowledgeID string,\n) (string, error) {\n\tphysical, err := s.inner.SaveFile(ctx, file, tenantID, knowledgeID)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tref, err := s.register(ctx, physical, tenantID, file.Filename, file.Size, false, \"\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif knowledgeID != \"\" {\n\t\tif err := s.catalog.Bind(ctx, ref, types.ResourceOwnerKnowledge, knowledgeID, types.ResourceRelationSourceFile); err != nil {\n\t\t\t_ = s.DeleteFile(ctx, ref)\n\t\t\treturn \"\", fmt.Errorf(\"bind stored resource: %w\", err)\n\t\t}\n\t}\n\treturn ref, nil\n}\n\nfunc (s *resourceCatalogFileService) SaveBytes(\n\tctx context.Context,\n\tdata []byte,\n\ttenantID uint64,\n\tfileName string,\n\ttemp bool,\n) (string, error) {\n\tphysical, err := s.inner.SaveBytes(ctx, data, tenantID, fileName, temp)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tsum := sha256.Sum256(data)\n\treturn s.register(ctx, physical, tenantID, fileName, int64(len(data)), temp, hex.EncodeToString(sum[:]))","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/resource_catalog.go#L86-L122","documentation":"In SaveFile, after a resource is stored and registered, an optional knowledgeID triggers catalog.Bind to link the resource to a knowledge entry as its source file. If Bind fails, DeleteFile is invoked to roll back the stored resource and this wrapped error is returned. The upload is fully undone; the caller must retry the whole operation.","triggerScenarios":"Calling SaveFile with a non-empty knowledgeID where the knowledge entry does not exist, was deleted concurrently, or the catalog DB rejects the ownership/relation insert.","commonSituations":"Stale knowledgeID passed by the client after the knowledge entry was removed, foreign-key constraint on the knowledge table, DB outage during the bind step, duplicate binding attempts.","solutions":["Verify the knowledgeID exists before calling SaveFile","Inspect the wrapped Bind error for DB/constraint causes","Retry the full SaveFile call — the partial resource was deleted","Remove the knowledgeID argument to store without binding if binding is optional"],"exampleFix":"// before\nref, err := svc.SaveFile(ctx, r, knowledgeID)\n// after\nif knowledgeID != \"\" {\n    if !knowledgeExists(ctx, knowledgeID) { return \"\", fmt.Errorf(\"unknown knowledgeID %q\", knowledgeID) }\n}\nref, err := svc.SaveFile(ctx, r, knowledgeID)","handlingStrategy":"validation","validationCode":"if knowledgeID != \"\" && !catalog.KnowledgeExists(ctx, knowledgeID) {\n    return fmt.Errorf(\"knowledge %q does not exist\", knowledgeID)\n}","typeGuard":null,"tryCatchPattern":"ref, err := svc.SaveFile(ctx, r, knowledgeID)\nif err != nil && strings.Contains(err.Error(), \"bind stored resource\") {\n    // stored resource was rolled back; refresh knowledgeID and retry full save\n}","preventionTips":["Validate knowledgeID existence before upload","Avoid caching knowledgeIDs client-side for long periods","Use the same transaction/session lifetime for knowledge creation and binding","Retry the entire SaveFile, not just the bind"],"tags":["catalog","bind","transaction","rollback"],"backgroundTag":"resource-bind-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}