{"record":{"id":"88510d638804261e","repo":"temporalio/temporal","slug":"w-t-expected-string-88510d","errorCode":null,"errorMessage":"%w: %T (expected string)","messagePattern":"%w: %T \\(expected string\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/persistence/sql/sqlplugin/postgresql/visibility.go","lineNumber":203,"sourceCode":"\t}\n\trow.StartTime = pdb.converter.FromPostgreSQLDateTime(row.StartTime)\n\trow.ExecutionTime = pdb.converter.FromPostgreSQLDateTime(row.ExecutionTime)\n\tif row.CloseTime != nil {\n\t\tcloseTime := pdb.converter.FromPostgreSQLDateTime(*row.CloseTime)\n\t\trow.CloseTime = &closeTime\n\t}\n\tif row.SearchAttributes != nil {\n\t\tfor saName, saValue := range *row.SearchAttributes {\n\t\t\tswitch typedSaValue := saValue.(type) {\n\t\t\tcase []any:\n\t\t\t\t// the only valid type is slice of strings\n\t\t\t\tstrSlice := make([]string, len(typedSaValue))\n\t\t\t\tfor i, item := range typedSaValue {\n\t\t\t\t\tswitch v := item.(type) {\n\t\t\t\t\tcase string:\n\t\t\t\t\t\tstrSlice[i] = v\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn fmt.Errorf(\"%w: %T (expected string)\", sqlplugin.ErrInvalidKeywordListDataType, v)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t(*row.SearchAttributes)[saName] = strSlice\n\t\t\tdefault:\n\t\t\t\t// no-op\n\t\t\t}\n\t\t}\n\t}\n\t// need to trim the run ID, or otherwise the returned value will\n\t// come with lots of trailing spaces, probably due to the CHAR(64) type\n\trow.RunID = strings.TrimSpace(row.RunID)\n\treturn nil\n}\n","sourceCodeStart":185,"sourceCodeEnd":217,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/persistence/sql/sqlplugin/postgresql/visibility.go#L185-L217","documentation":"PostgreSQL counterpart of the MySQL check: processRowFromDB wraps sqlplugin.ErrInvalidKeywordListDataType when a keyword-list search attribute element decoded from the DB is not a string. The message includes the actual Go type found. Indicates the stored search-attributes JSON does not match the keyword-list schema expectation.","triggerScenarios":"SelectFromVisibility/GetFromVisibility on PostgreSQL reading a row where a keyword-list attribute's []interface{} contains a non-string item — corrupted JSON, non-string values written by a client, or manual data edits.","commonSituations":"Rows written before validation existed; manual psql edits of search attributes; clients pushing numeric/bool values into keyword-list custom search attributes.","solutions":["Locate the malformed row and attribute (type shown in the message) and correct the JSON in the visibility table.","Validate keyword-list values are strings at write time.","Check for writer-side bugs that serialize non-strings for keyword-list fields.","If widespread, run a migration script sanitizing keyword-list values to strings."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Go: enforce string-only keyword lists on write\nfor k, vals := range keywordLists {\n    for _, v := range vals {\n        if _, ok := v.(string); !ok { return fmt.Errorf(\"%s: non-string keyword value\", k) }\n    }\n}","typeGuard":"func asStrings(items []interface{}) ([]string, bool) {\n    out := make([]string, len(items))\n    for i, v := range items {\n        s, ok := v.(string)\n        if !ok { return nil, false }\n        out[i] = s\n    }\n    return out, true\n}","tryCatchPattern":"// Go\nif err != nil && errors.Is(err, sqlplugin.ErrInvalidKeywordListDataType) {\n    // identify and sanitize the offending row\n    return nil, err\n}","preventionTips":["Validate keyword-list values client-side before persistence","Run data-sanitization migrations after legacy upgrades","Log the %T detail to locate malformed columns quickly","Add unit tests covering non-string keyword values in scan paths"],"tags":["postgresql","search-attributes","data-corruption","type-mismatch","keyword-list"],"backgroundTag":"invalid-keyword-list-data-type","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}