{"record":{"id":"3cd2303c4127007d","repo":"hcengineering/platform","slug":"workspace-is-missed","errorCode":null,"errorMessage":"workspace is missed","messagePattern":"workspace is missed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"foundations/stream/internal/pkg/storage/storage.go","lineNumber":71,"sourceCode":"\n// MultipartPart represents uploaded multipart part\ntype MultipartPart struct {\n\tPartNumber int    `json:\"partNumber\"`\n\tETag       string `json:\"etag\"`\n}\n\n// MultipartStorage represents multipart-based storage\ntype MultipartStorage interface {\n\tMultipartUploadStart(ctx context.Context, objectName, contentType string) (string, error)\n\tMultipartUploadPart(ctx context.Context, objectName, uploadID string, partNumber int, data []byte) (*MultipartPart, error)\n\tMultipartUploadComplete(ctx context.Context, objectName, uploadID string, parts []MultipartPart) error\n\tMultipartUploadCancel(ctx context.Context, objectName, uploadID string) error\n}\n\n// NewStorageByURL creates a new storage based on the type from the url scheme, for example \"datalake://my-datalake-endpoint\"\nfunc NewStorageByURL(ctx context.Context, u *url.URL, storageType, token, workspace string) (Storage, error) {\n\tif workspace == \"\" {\n\t\treturn nil, errors.New(\"workspace is missed\")\n\t}\n\tswitch storageType {\n\tcase \"datalake\":\n\t\tif token == \"\" {\n\t\t\treturn nil, errors.New(\"token is missed\")\n\t\t}\n\t\treturn NewDatalakeStorage(ctx, u.String(), workspace, token), nil\n\tcase \"s3\":\n\t\treturn NewS3(ctx, u.String(), workspace), nil\n\tdefault:\n\t\treturn nil, errors.New(\"unknown scheme\")\n\t}\n}\n\nfunc getContentType(objectKey string) string {\n\tif strings.HasSuffix(objectKey, \".ts\") {\n\t\treturn \"video/mp2t\"\n\t}","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/stream/internal/pkg/storage/storage.go#L53-L89","documentation":"NewStorageByURL creates a Storage implementation based on the URL scheme (e.g. datalake://). It requires a non-empty workspace name to scope all object operations, and throws this error before any storage backend is contacted when the workspace argument is an empty string.","triggerScenarios":"Calling NewStorageByURL with workspace=\"\" — typically because the workspace value came from an unset config field, an empty environment variable, or a request context where the workspace was never resolved.","commonSituations":"Deployments with a missing WORKSPACE env var; callers that extract workspace from a token or URL but pass the empty default; refactoring where a caller forgot the newly added workspace parameter.","solutions":["Pass a non-empty workspace string to NewStorageByURL","Verify the config/env variable that supplies the workspace is set before calling","Log or validate the workspace value at the call sites (stg, processTask, Transcode) before constructing storage"],"exampleFix":"// before\nstorage, err := NewStorageByURL(ctx, u, \"datalake\", token, cfg.Workspace) // cfg.Workspace == \"\"\n// after\nif cfg.Workspace == \"\" {\n    return fmt.Errorf(\"WORKSPACE must be configured\")\n}\nstorage, err := NewStorageByURL(ctx, u, \"datalake\", token, cfg.Workspace)","handlingStrategy":"validation","validationCode":"if workspace == \"\" {\n    return nil, fmt.Errorf(\"storage: workspace must be provided\")\n}\nstorage, err := NewStorageByURL(ctx, u, storageType, token, workspace)","typeGuard":null,"tryCatchPattern":"storage, err := NewStorageByURL(ctx, u, storageType, token, workspace)\nif err != nil {\n    return fmt.Errorf(\"init storage: %w\", err)\n}","preventionTips":["Validate config (workspace, token) at startup before constructing storage","Fail fast with clear messages when env vars are empty","Add a unit test asserting NewStorageByURL rejects empty workspace"],"tags":["go","validation","configuration","storage"],"backgroundTag":"missing-config-parameter","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}